IThome 2024 鐵人賽 一直刷 CTF - Day20

前言

本日進度:

上課紀錄

Lab - swirl

stage 1 & 2

這兩題跟之前 Lab - phpisbest 差不多,只要在後面加上 /?A[]=[0]&B[]=[3] 就能達成各種 null==null 就能過關了

stage 3

這題我有觀察到可以 path traversal,但是我亂戳戳不到東西,過了大概十分鐘,又沒忍住點了一下 hint ,才發現要戳的東西就是寫在原始碼裡面的 config.php ,就能前往下一關了

Image
Image

stage 4

這題她沒有一個地方讀取 👀 ,但有個 extract($_POST) 可以利用 POST 的方式輸入 👀 ,之後就跟 lfi2rce 一樣,利用 php filter chain 構造出 webshell ,就能得到 Flag 了

Image
Image

Note: 這邊要注意的是這個 webshell 指令最後需要的參數 1,他是透過 $_GET[1] 來取得的,所以要寫在網址後面解析,不是寫在 POST 裡面

Lab - fakelog

他很好心的給我們看了他生成的原始碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/local/bin/python3
import sys

if __name__ == '__main__':
if len(sys.argv) < 2:
print("no argument")
sys.exit()

magic_num = int(sys.argv[1]) if sys.argv[1].isdigit() else sys.argv[1]
if magic_num == 0:
print("0 is not allowed")
sys.exit()
while magic_num != 1:
with open(f"fakelog/{magic_num % 6 if isinstance(magic_num, int) else magic_num}", "r", encoding="utf-8") as fd:
print(fd.read())
magic_num = 3 * magic_num + 1 if magic_num % 2 else magic_num // 2

重點有兩個

  1. magic_num = int(sys.argv[1]) if sys.argv[1].isdigit() else sys.argv[1]
  2. with open(f"fakelog/{magic_num % 6 if isinstance(magic_num, int) else magic_num}", "r", encoding="utf-8") as fd:

他不會檢查 magic number 是文字,而且在他是文字的時候他還會直接讓我們讀取檔案,所以我們可以利用這個漏洞來做 path traversal

又可以從 Hint 的 dockerfile 中知道有個叫 main.py 是主程式,拿出來看看

1
2
3
4
5
6
7
8
9
10
11
12
FROM python:3.10
RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/etc/poetry python3 -
WORKDIR /app

COPY pyproject.toml poetry.lock .
RUN /etc/poetry/bin/poetry config virtualenvs.create false && \
/etc/poetry/bin/poetry install
COPY . .
ARG FLAG
RUN echo $FLAG > /app/flag_`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1`
USER daemon
ENTRYPOINT ["python", "/app/main.py"]
Image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
result:
from flask import Flask, render_template, request, send_file
import subprocess

app = Flask(__name__)


@app.route('/')
def home():
return render_template('home.html')


@app.route('/howtogen')
def howtogen():
return send_file('gen.py', mimetype='text/plain', as_attachment=False)


@app.route('/hint')
def hint():
return send_file('Dockerfile', mimetype='text/plain', as_attachment=False)


@app.route('/gen', methods=['GET', 'POST'])
def get_input():
message = None
if request.method == 'POST':
user_input = request.form['user_input']
if " " in user_input:
result = "evil input"
else:
result = subprocess.run(
f"python gen.py {user_input}", shell=True, capture_output=True, text=True).stdout.replace('\', '')
message = f"result: {result}"
return render_template('generate.html', message=message)


if __name__ == "__main__":
app.run(host='0.0.0.0', port=5050)

可以知道不能輸入空白,另外還有他執行程式的方法是

1
2
subprocess.run(
f"python gen.py {user_input}", shell=True, capture_output=True, text=True).stdout.replace('\', '')

可以讓我們利用類似 ;id 這樣的方式來達到 RCE,空白就用 ${IFS} 來代替,隨便戳戳看之後就能拿到 Flag 了

Image
Image
Image

參考資料