前言:

很开心能收到群友的跨年礼物,虽然没做出来。
虽然这只是入门题,但是作者也没学过CTF,所以题解也不完整。
事实上咱真正想说的并不是题解,但是没办法,自己之前定下的规矩,博文必须有技术性内容,所以正好趁着做这套题水一篇博客。

环境:

题目说有docker就能做,不过很可惜,咱手机上的docker寄了,也懒得重装,咱就直接rurima启动了好吧,不管怎么说容器理论这方面直接遥遥领先了好吧。
直接一个:

1
./rurima-dbg docker pull -i ricky8955555/rknazo-2025 -s ./rknazo

好了,看到run.py的那一刻,我就知道没戏了。不过既然已经花流量下载了,我们硬着头皮做一做吧还是。

解题:

题目在/rknazo里面,第一次做CTF题还怪猎奇的说。

0x0:

第一道说有一个user持有flag,我们就直接先看用户配置就是了。首先第一想到的肯定是passwd文件,也确实在里面。groups和shadow是没有东西的,至于subuid这东西,不写rootless容器的谁看啊🌚
cat /etc/passwd,输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
flag:x:1000:1000:666c61677b36626564353336662d626465322d303063322d666130302d3663313036306234343762367d:/dev/null:/sbin/nologin

大佬说是hex存储的,咱也不太懂怎么用命令行去解析,直接把上一篇文章写过的代码复制了下,还真能用,大家看https://blog.crack.moe/2024/11/09/hex-to-file/ 这篇就好了,别说还挺巧的。
于是直接拿flag,来到下一题。

0x1:

说是“只有 ‘flag’ 符号链接最终指向的文件名是正确的且可用 于解密出最终结果”,realapth一下确实能得到一个flag为文件名,但是看群里貌似有做错这一道的,直接realpath出来到底对不对暂时存疑。

0x2:

把标准错误扔掉, 然后把标准输出丢给文件. 我会走标准输 出把 flag 给你的~
这个很简单了,./challenge 2>/dev/null >c然后cat c直接拿结果,我们来到下一题。

0x3:

say_millions_of_yes_to_me,看题目就知道是啥了,yes y|./challenge

0x4:

04-sleep_lover,这一道直接硬控我。
我有个懒鬼孩子, 你可以去叫醒他问问
显然要去看子进程,strace下真的有。
然后开始了唐完了的解题过程。
首先,他说要叫醒,我们想到了什么,sleep(),好!直接一个so文件把sleep()改成直接return,然后上LD_PRELOAD大法。
虽然并不管用。
然后strace发现了系统调用

1
rt_sigaction(SIGINT, {sa_handler=0x63a8bb5d20, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7cc2474b10}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0

似乎是和这个有关系的,但是咱没思考rt_sigaction()是什么,还以为是他给子进程发了个信号呢,当时就想,我直接屏蔽这个系统调用试试🤓☝️
于是直接上seccomp好吧(虽然是错的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
perror("seccomp_init");
exit(1);
}
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(0), SCMP_SYS(rt_sigaction), 0);
if (seccomp_load(ctx) < 0) {
perror("seccomp_load");
exit(1);
}

system("./challenge");
return 0;
}

然后试过了发现,没用🌚
然后试着LD_PRELOAD大法替换了下signal()函数:

1
2
3
4
5
6
#include <stdio.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler){
printf("reg sig: %d\n",signum);
handler(signum);
}

然后运行,发现注册了信号SIGINT,但是为什么call一下这个handler不能直接出flag咱也不是很清楚。
最后用SIGINT去kill一下子进程就出flag了,不得不说,给自己唐完了。

0x5:

解的过程依旧很唐。去string那个so文件没发现啥,查看符号表发现有open()和getflag(),当时还在想我能不能直接把这个getflag()给他call出来,后来不知道原型所以放弃了。
实际上直接LD_PRELOAD加载那个库,然后cat flag就好了,应该是在被替换的read()函数里判断的,但是,我没思考,愣是直接运行cat,以为他判断的是文件输入,试了半天才知道,判断的是文件名。。。。。。。。。。。。
(后来群友说其实是能直接call那个函数得到flag的,因为忘了去符号了,我是天才好吧)

0x6:

最后一关根本不会好吧。他说查看日志,我也知道日志在/var/log,我还知道服务器是.daemon,rust写的,剩下的,就不知道了。
没学过网络相关,自己怕是也没有网安的天赋,输的不冤。不过虽然输了,咱依旧挺开心的😇

写在最后:

最终我还是放弃了最后那道题,正如我人生中做出的许许多多的放弃的决定。我知道我没有能力去将它解出来,做前面的题目时,发现自己从零基础已经进步了许多,这已经足够了。最后一道题当作留白,我依旧可以很开心的,用这套题与2024,也与自己人生中过去的18个年头做一个完美的告别。
事实上,人生并不是考卷,也没人能做到完美。真正有资格去解读,去评判自己的人生,自己的行为的人,从来都只有我自己。其实从小我便学会了放弃,如今也是彻底学会了放下自己放弃的行为。有时候放弃并不意味着没有取得什么,只是自己去追求其他的东西去了而已。学会取舍,人生苦短,及时行乐,切忌称为整天阴着脸的做题家。
2024,全民焦虑的一年,我能速通一遍焦虑与躯体化,也挺不容易的。我现在感到无比的幸福,我相信,我是被世界所祝福的孩子。
不过也挺感谢心理疾病的,咱甚至疯狂到了可以去享受败北直接带来的快感,M属性大爆发:Moe-hacker好吧🤓☝️
虽然放弃了高考,咱也在这里享受了一把写作文的快感,人生是开放性问题,写起来可比答卷舒服多了。不管怎么说,2025,加油😇