[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를 찾는다.

 

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

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.