论坛首页 逆向工程技术区 阅读主题

[原创] 第八届“强网”拟态防御国际精英挑战赛 - WIN!致敬mt 复现

441 浏览 3 回复
#1 楼主 2026-06-01 21:09:19
前言漏洞分析基本信息解密admin的账号密码栈溢出目录截断字符逃逸命令执行exploit总结拟态比赛的时候时间过于匆忙,并没有很多时间看这道题目,因此赛后进行了复现,这一过程中十分感谢@zikh26师傅和神秘的@Nik Xe大哥给予的帮助。为什么第二道iot还没复现,问就是c++还没逆完从启动脚本发现其实只开了一个80端口,我们先启动看一下基本的信息,可以发现80端口由lighttpd监听,启动方式为/usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf 接着看对应的配置文件,可以看到对应的根目录,从根目录下我们可以找到cgi和部分二进制文件修改下启动的参数,增加两个端口,22用于ssh连接,1234用于gdbserver的调试尝试访问登陆页面抓包看下,发现登录逻辑和auth.cgi有关把对应的lighttpd文件和cgi复制到宿主机分析auth.cgi,发现一个存储账号密码的文件不难看出这里的密码需要解密,跟踪下文件打开的逻辑,会发现最后有一个sprintf打印这一段数据,因此推断上面的逻辑是读取文件,所以这里可以尝试动调验证一下下断点之后发现,确实是读取了admin加密后的密码,继续分析ida可以发现下方有一段strcmp的逻辑,经过分析可以发现其实是讲我们的输入密码进行加密后与admin加密后的密码进行对比加密的算法比较简单,核心逻辑如下,先进行xor,然后将最后的结果base64加密,因此可以根据加密的后的密码计算出原本admin的密码解密脚本密码为admin/8g323##a08h33zx33@!B!$$$$$$$通过admin账号登陆后台后,可以去访问很多其他的功能这里我从manage.cgi开始分析,可以发现一些值得关注的地方,比如说这里创建了一个共享内存,接着会用一个bss段上的指针去指向这个共享内存这里人为传入的rk参数如果与/tmp/rootkey的内容一致,则会进入下方的函数逻辑,继续跟进这里仔细看一下就发现tv.tv_usec & 1的值无非0或1,所以一定会调用到sub_9FD0函数sub_9FD0函数首先会把/tmp/store/id.txt文件的id值读出来,也就是下方的v16。接着调用到shm_mem_dump,这里会根据传入的第三个参数来决定采用哪一种memcopy的模式,第一次调用是正常的memcpy调用,将共享内存的内容读到堆上。接着这里的(*(&buf + (((unsigned __int8)v16 ^ 0x13) & 1)),由于 & 1操作的存在,因此还是会调用到shm_mem_dump函数,但是此时的v16其实int类型,所以这里如果传入一个负数,就会导致一个溢出,由于是拷贝到v19,而v19是一个栈上的值,所以会造成栈溢出结合上方的分析,我们可以发现,我们这里的栈溢出是有前置条件的所以接下来再分析的同时还需要解决这两个问题分析upload.cgi文件当action为download的时候,可以设置path字段,然后下面存在一个snprintf函数,这里其实存在一个截断的问题,同时注意check_suffix(filepath, "nik.gif")会检查文件的后缀是不是nik.gif,那么其实可以尝试去构造一个路径,类似于/tmp/./././xxxxxrootkeynik.gif的形式,这样保证0x60截断了nik.gif,同时绕过后缀的检查,然后同时还可以访问到/tmp/rootkey文件,接着下面读取文件内容,同时满足文件大小是64b,然后会将内容输出但是这里存在一个问题就是/tmp/rootkey这个文件在默认状态下不存在,所以我们需要找到创建这个文件的逻辑可以通过grep -r查找,发现了watch中存在rootkey这个字符串分析这个binary可以发现,这里会创建一个64b大小的文件,然后再重命名为/tmp/rootkey,最后输出Good luck那么通过这样,我们可以创建/tmp/rootkey文件,同时通过目录截断读取这个文件的内容,也就是我们知道了niksessid,这样我们可以提前设置rk的值,绕过判断,触发漏洞逻辑通过分析lang.cgi,我们可以通过设置setid字段设置id的值,然后这个值就会被保存到/tmp/store/id.txt文件里那么至此为止,我们已经具备了触发栈溢出的能力了,那么现在的问题是如何写rop回头看栈溢出的位置,其实是拷贝共享内存的内容到栈上,那么现在就需要接着去看共享内存的值应该如何设置这里会根据我们传入的action来决定执行什么函数这是action_dispatch_table的内容问题出现在setpkfunc中,这里会将/tmp/store/publicfile.txt的内容读出来,仔细看会发现存在一个off-by-one但是其实根本没用接着这里会有一个转化,就是转化为16进制的操作,同时仍然存在off-by-one接着的操作比较迷惑,一些奇怪的判断,然后需要注意的是当idx == 80时,会直接执行final_data[81] = chunk[80];然后退出循环,也就意味着chunk[80]不需要经过前面的字符检查,直接被赋值到final_data[81]下面就可以通过设置cnt1和cnt2来将我们逃逸的字符写到共享内存上由于这题没有pie和aslr,所以可以直接获取到gadget,同时出于稳定性考虑,我们可以直接把system需要执行的字符串写到共享内存上,这样更稳定,然后作为system的参数,这样就可以稳定的rce了需要注意的是,我们需要将flag重定向到/tmp/store/logs.txt,然后通过log的查看功能来看到flag这个脚本需要分开执行,先执行下面,同时保证rootkey是可见字符(如果不是,则会让最后的漏洞无法触发)接着执行,就可以拿到flag总体来看其实难度并不算大,但逆向上会耽误一点时间,主要是对于总体的把控,比如说每一个cgi有什么功能,我怎么去触发,哪些功能有问题,并巧妙地将孤立的微小漏洞组合起来,形成一条完整的 RCE 攻击链root@debian-armel:/# ps -ef

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288945.htm
#2 2026-06-01 21:09:19
分析的太细致了师傅,感谢分享
#3 2026-06-01 21:09:19
感谢分享,很细节的题目,算是目前CTF比赛中少见的接近于真实IoT固件的环境。麻烦上传一下题目附件~
#4 2026-06-01 21:09:19
题目附件链接: e8fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4c8X3E0H3k6h3k6q4c8f1u0S2M7$3W2W2K9h3#2n7y4r3k6e0j5#2y4z5k6#2)9K6c8Y4m8%4k6q4)9K6c8o6W2I4x3Y4N6Q4x3U0k6F1j5Y4y4H3i4K6y4n7i4@1f1$3i4K6S2r3i4K6V1H3i4@1f1#2i4K6S2r3i4K6V1$3i4@1f1%4i4@1p5H3i4K6R3I4i4K6y4m8i4K6t1$3L8X3u0K6M7q4)9K6b7U0W2I4x3Y4N6Q4x3U0k6F1j5Y4y4H3i4K6y4n7

请登录后参与讨论

立即登录 注册账号