오랜만에 CTF에 참가하여 WEB 문제 중, php의 `sprintf()` 함수를 이용한 문제가 있었다.

 

아래 코드를 보면, SQL `addslashes()` 함수 때문에 SQLI 가 불가능한 것처럼 보이지만, `sprintf()` 함수에서 format string을 이용하면 이를 bypass 할 수 있다. 이를 설명하기 위해 글을 계속 읽어보자.

<?php 
    error_reporting( E_ALL );
    ini_set( "display_errors", 1 );

    $user = sprintf("%s", addslashes($_GET["user"]));
    $password = md5($_GET["password"]);

    echo $user . "<br>";
    echo sprintf("SELECT * FROM user WHERE user_id='$user' AND password='%s'", $password);
?>

 

아래 코드를 보면, $name 과 $money 변수의 값을 서식 문자로 받아 출력이 된다.

<?php
    $name = "John";
    $money = "500";
    echo sprintf('The %s has %s dollar.', $name, $money);
?>

# The John has 500 dollar.

 

 

위 코드의 서식 문자를 아래 코드처럼 인자의 순서를 지정할 수 있다.
(이는 C언어에서 Format String Bug를 이용한 explit 할 때도 사용된다.)

<?php
    $name = "John";
    $money = "500";
    echo sprintf('The %1$s has %2$s dollar.', $name, $money);    # Change
?>

# The John has 500 dollar.

 

 

 

위 예제에서는 코드에 전혀 문제가 없다. 하지만 `sprintf()` 함수를 다음과 같은 사례로 2번 이상 사용하게 된다면 어떻게 될까

5번째 줄에서, `sprintf()` 함수의 결과를 `$user` 변수에 저장하고, 이를 9번째 줄에서 query 문에 사용된다.

<?php 
    error_reporting( E_ALL );
    ini_set( "display_errors", 1 );

    $user = sprintf("%s", addslashes($_GET["user"]));
    $password = md5($_GET["password"]);

    echo $user . "<br>";
    echo sprintf("SELECT * FROM user WHERE user_id='$user' AND password='%s'", $password);
?>

 

 

만약 공격자가 user 파라미터에 `%s` 라는 문자열을 전송할 경우 서버는 다음과 같이 에러를 출력한다.

`sprintf()` 함수에서 인자의 개수가 작아서 발생한 에러이다.

즉, 서버 입장에서 보면 user 변수의 값이 %s 이므로 다음과 같은 코드가 동작하려가다 에러가 발생한 것이다.

echo sprintf("SELECT * FROM user WHERE user_id='%s' AND password='%s'", $password);

 

해당 에러를 발생시키지 않기 위해, 위에서 설명한 `%1$s` 를 사용하여 첫번째 인자를 사용하도록 만든다.

결국엔, user_id에는 password 변수의 값이 들어간 것을 볼 수 있다.

 

바로 위에서 입력한 `%1$s` 에서 s만 빼고 `%1$` 만 입력 한다면, 신기한 결과를 얻을 수 있다.

$ 다음에는 s나 d와 같은 서식 문자의 정의가 있어야 하는데, 이러한 문자가 없어서 기존에 있던 싱글쿼터를 먹어버리게 된 것이다. 마찬가지로 \(역 슬레시) 문자도 먹어버린다.

 

위 상황을 이용하여 `%1$'` 라는 문자열을 전송하게 되면, 다음과 같이 `addslashes()` 함수로 역 슬레시가 붙어 있었지만, SQL query 문에서는 없어진 것을 볼 수 있다.

 

 

 

 

Reference

https://pino-hd.github.io/2018/06/19/%E7%94%B1%E4%B8%80%E6%AC%A1CTF%E5%BC%95%E5%8F%91%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8BPHP%E7%9A%84sprintf%E5%87%BD%E6%95%B0/