[SECCON 2017] Web Challenge review

Log Search[link]

 

들어가면 화면 아래 쪽에 “http://logsearch.pwn.seccon.jp/logsearch.php“링크가 있다.

해당 링크로 들어가면 현재 검색된 로그들이 주르륵 뜬다.
여기서 flag파일을 찾으란다.

우리가 원하는건 접근 경로에 flag가 들어가고 반응이 200인거니
flag 200 을 처보았지만 다른 로그들에 뭍혀 flag 로그가 나오지 않는다.

이걸 request:flag AND response:200이렇게 쳐줘야 flag 200 접근이 되는 로그가 나오며 flag를 볼 수 있다.

아니 저걸 저렇게 검색하는 방법을 어찌 알라는건지 모르겟다..

 


SqlSRF[link]

#!/usr/bin/perl

use CGI;
my $q = new CGI;

use CGI::Session;
my $s = CGI::Session->new(undef, $q->cookie('CGISESSID')||undef, {Directory=>'/tmp'});
$s->expire('+1M'); require './.htcrypt.pl';

my $user = $q->param('user');
print $q->header(-charset=>'UTF-8', -cookie=>
  [
    $q->cookie(-name=>'CGISESSID', -value=>$s->id),
    ($q->param('save') eq '1' ? $q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M') : undef)
  ]),
  $q->start_html(-lang=>'ja', -encoding=>'UTF-8', -title=>'SECCON 2017', -bgcolor=>'black');
  $user = &decrypt($q->cookie('remember')) if($user eq '' && $q->cookie('remember') ne '');

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) {
      if($row[0] ne '' && $q->param('pass') ne '' && $row[0] eq &encrypt($q->param('pass'))) {
        $s->param('autheduser', $q->param('user'));
        print "<scr"."ipt>document.location='./menu.cgi';</script>";
        $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="background:#000 url(./bg-header.jpg) 50% 50% no-repeat;position:fixed;width:100%;height:300px;top:0;">
</div>
<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"><input type="checkbox" name="save" value="1">Remember Me</td></tr>
<tr><td colspan="2" align="right"><input type="submit" name="login" value="Login"></td></tr>
</table>
</form>
</div>
</body>
</html>
EOM

1;

문제 제목에서도 알수 있다 싶이 SQL injection 문제이다.

my $sth = $dbh->prepare("SELECT password FROM users WHERE username='".$q->param('user')."';"); 부분을 보면 prepare을 하지만 문자열을 직접 쿼리에 넣고 있으므로 injection으로 부터 취약하다.
이를 통해 Time based SQL injection을 하면 된다.

SQL injection을 통해 알아낸 admin의 password 는 “d2f37e101c0e76bcc90b5634a5510f64” 온라인에 존재하는 md5 테이블에 없다.
하지만 “remember” 쿠키에 내용이 디크립트 되어 Username input tag의 value로 들어가게 된다.
이를 통해 알아낼 수 있는건 d2f37e101c0e76bcc90b5634a5510f64는 “Yes!Kusomon!!” 이다.

SECCON 2017

25, 22, 80 포트가 열려있다.

SECCON 2017

wget 실행시 wget 버전과 리퀘스트의 결과가 나온다.

ID: admin, PW: Yes!Kusomon!! 으로 로그인을 하게 되면 “netstat”와 wget을 실행 할 수 있는 페이지가 뜬다.
command injection은 안되니 다른 방법을 찾으면, wget가 버전이 낮다 즉 문제의 제목처럼 wget의 bug를 사용할 수 있다. (SSRF)

Payload : 127.0.0.1%0d%0aTest%3a %0aHELO 127.0.0.1%0aMAIL FROM%3a%3csilnex%40silnex.kr%3e%0aRCPT TO%3a%3croot%40localhost%3e%0aDATA%0aFrom%3a silnex%40silnex.kr%0aTo%3a root%40localhost%0aSubject%3a give me flag%0d%0a.%0d%0a%0aQUIT%0a:25

SMTP 포트가 열려있는 것을 확인 햇다면, 해당 취약점을 통해 메일을 보내자.
그러면 encrypt 된 37208e07f86ba78a7416ecd535fd874a3b98b964005a5503bcaa41a1c9b42a19 문자열이 오는데 ,
이를 admin의 password를 decrypt한 것처럼 “remember” 쿠키에 넣게 되면 SECCON{SSRFisMyFriend!}flag가 나오게 된다.


automatic_door[link]

<?php
$fail = str_repeat('fail', 100);
$d = 'sandbox/FAIL_' . sha1($_SERVER['REMOTE_ADDR'] . '95aca804b832f4c329d8c0e7c789b02b') . '/';
@mkdir($d);

function read_ok($f)
{
    return strstr($f, 'FAIL_') === FALSE &&
        strstr($f, '/proc/') === FALSE &&
        strstr($f, '/dev/') === FALSE;
}

function write_ok($f)
{
    return strstr($f, '..') === FALSE && read_ok($f);
}

function GetDirectorySize($path)
{
    $bytestotal = 0;
    $path = realpath($path);
    if ($path !== false && $path != '' && file_exists($path)) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object) {
            $bytestotal += $object->getSize();
        }
    }
    return $bytestotal;
}

if (isset($_GET['action'])) {
    if ($_GET['action'] == 'pwd') {
        echo $d;

        exit;
    }
    else if ($_GET['action'] == 'phpinfo') {
        phpinfo();

        exit;
    }
    else if ($_GET['action'] == 'read') {
        $f = $_GET['filename'];
        if (read_ok($f))
            echo file_get_contents($d . $f);
        else
            echo $fail;

        exit;
    } else if ($_GET['action'] == 'write') {
        $f = $_GET['filename'];
        if (write_ok($f) && strstr($f, 'ph') === FALSE && $_FILES['file']['size'] < 10000) {
            print_r($_FILES['file']);
            print_r(move_uploaded_file($_FILES['file']['tmp_name'], $d . $f));
        }
        else
            echo $fail;

        if (GetDirectorySize($d) > 10000) {
            rmdir($d);
        }

        exit;
    } else if ($_GET['action'] == 'delete') {
        $f = $_GET['filename'];
        if (write_ok($f))
            print_r(unlink($d . $f));
        else
            echo $fail;

        exit;
    }
}

highlight_file(__FILE__);

파라미터에 action=write과 filename을 보내면 action=pwd에 나오는 경로에 저장해준다.

  1. .htaccess 파일에 .html 등 다른 확장자에서도 php 코드가 실행 되게한다.
    AddType application/x-httpd-php .html .htm이렇게 하면 html파일과 htm파일에서도 php코드가 실행 된다.
  2. 리버스쉘이든 웹쉘이든올린다.
  3. flag를 찾는다.

 

글의 문제가 있다면 댓글을 달아 주세요.

This site uses Akismet to reduce spam. Learn how your comment data is processed.