본문 바로가기

챌린지/Wargame.kr

bughela - strcmp

wargame.kr의 7번째 문제인 strcmp다.

이번 문제는 strcmp 함수를 우회하는 것이 목적이다. Start 버튼을 눌러 문제를 확인해보자.

간단한 입력폼 하나와 소스 코드를 보는 링크가 있다. POST로 비밀번호를 보낸다는 것 말고는 얻을 수 있는 정보가 없으니 소스를 분석해보자.

<?php
    require("../lib.php"); // for auth_code function

    $password = sha1(md5(rand().file_get_contents("/var/lib/dummy_file")).rand());

    if (isset($_GET['view-source'])) {
        show_source(__FILE__);
        exit();
    }else if(isset($_POST['password'])){
        sleep(1); // do not brute force!
        if (strcmp($_POST['password'], $password) == 0) {
            echo "Congratulations! Flag is <b>" . auth_code("strcmp") ."</b>";
            exit();
        } else {
            echo "Wrong password..";
        }
    }

?>

$password를 생성한 후 if 조건문을 타고 있는데 위의 분기는 소스 코드를 보여주는 동작이므로 아래쪽을 보면 된다. 브루트 포스 방지를 위해 1초간 대기한 후 사용자가 입력한 password 값과 생성된 $password가 같은지 비교하여 플래그를 제공하고 있는데, 그렇다면 $password는 어떻게 생성되는지 알아보자.

 

이런저런 함수를 호출하고 있지만 결론부터 말하자면 이는 절대로 사용자가 예측할 수 없도록 생성한 비밀번호다. 보면 $password는 sha1(md5(rand().file_get_contents("/var/lib/dummy_file")).rand())로 생성되고 있는데 이는 난수 + dummy_file의 내용 + 난수로 구성된 문자열을 md5, sha1 해싱하고 있다. 그래서 이를 알아내기란 거의 불가능하며 그렇다면 사용자가 이 비밀번호를 똑같이 입력해서 strcmp()를 통과할 방법이 없다는 얘기가 된다. 그렇다면 어떻게 우회할 수 있는 것일까?

 

제일 먼저 살펴볼 것은 strcmp() 함수가 어떤 상황일 때 어떤 값을 반환하는지다. 함수 자체는 C언어와 비슷하기 때문에 두 문자열이 일치하면 0을 반환하고 그렇지 않으면 음수나 양수를 반환하는데 이는 피연산자1에서 피연산자2를 뺄셈하여 얻은 결과다. 즉 문자열이 일치한다면, 예를 들어 "a"와 "a"를 비교했을 경우 둘 다 ASCII 값으로 97이기 때문에 97 - 97 = 0이 된다. 그래서 문자열이 일치한다면 0을 반환하는 것이다. 그것 말고도 다른 경우는 없을까? 한번 PHP의 문서를 읽어보기로 했다.

 

PHP: strcmp - Manual

 

www.php.net

문서 자체에도 반환값은 두 문자열을 비교해서 음수, 0, 양수로 반환하는 것 말고는 별다른 정보가 적혀있지 않았다. 그런데 유저들이 남긴 노트란에 신경쓰이는 내용이 있었는데 strcmp 함수를 사용할 때 두 매개변수가 모두 확실하게 문자열이 아닌 이상 이 함수를 사용하는 것은 예기치 않은 결과를 불러올 수 있다는 것이다. 이게 무슨 소리인가 해서 자세히 살펴보니 다음과 같은 비교 예시를 볼 수 있었다.

즉, strcmp() 함수에서 매개변수로 문자열이 아닌 다른 형식의 값이 올 경우 함수의 반환값은 우리가 예상한 것과 다르게 나타날 수 있다는 것이다. 이에 관해서 좀 더 조사해보니 다음과 같은 를 확인할 수 있었다.

 

PHP: PHP type comparison tables - Manual

Some function to write out your own comparisson table in tsv format. Can be easily modified to add more testcases and/or binary functions. It will test all comparables against each other with all functions. '==',        'ne' => '!=',        'gt' =>

www.php.net

여기서 보면 놀랍게도 분명히 서로다른 자료형임에도 비교 결과가 true가 되는 경우가 꽤 많이 있는것을 볼 수 있었다. 등호를 세 개 사용하는 엄격한 비교(===)에서는 흔히 생각하는 것처럼 같은 자료형의 같은 값만 비교 결과가 true가 나왔지만 등호를 두 개 사용하는 느슨한 비교(==)에서는 위처럼 true가 간간히 보인다. 이런걸 보면 php는 c언어와 참 닮은것 같기도 한데... 아무튼 그렇다면 이를 활용해서 strcmp() 함수에 매개변수를 서로다른 자료형을 갖도록 하면 true, 즉 0을 반환해서 저 로직을 우회할 수 있으리라 추측해볼 수 있다.

그런데 결국 strcmp()에는 사용자가 POST입력으로 보낸 데이터가 그대로 들어가기 때문에 우리가 일반적으로 입력하는, 즉 폼에 데이터를 입력하고 버튼을 눌러 보내는 방식으로는 문자열 데이터밖에 전송되지 않아 같은 자료형인 $password와 항상 정상적으로 비교된다. 그러면 어떻게 문자열이 아닌 다른 자료형의 데이터를 전달해줄 수 있을까? 검색해보면 이런 자료들을 금방 찾을 수 있었다.

 

php $_POST Array 데이터 받기

value1=2012251&value1=2012254&value2=531&value2=544데이터를 위 방법으로 붙여서 던지면 겹쳐져서 Array 로 받아 올수 있다고 봤는데 실제로 해보니 맨 마지막 값만 불러오게 되었습니다. array 로 받

hashcode.co.kr

폼으로 전달되는 요소의 이름을 'password[]' 처럼 바꿔주면 이를 php에서 배열로 인식한다고 한다. 그래서 다음과 같이 HTML 코드를 수정해서 배열로 바꿔주고 입력폼에 아무 값이나 입력해서 버튼을 누르면 플래그를 볼 수 있다.

 

 

이번 문제에서는 함수가 무조건 완전하게 동작한다는 틀에 박힌 사고방식때문에 푸는데 시간이 꽤 걸린 문제였다. C언어에서든 어디서든 strcmp() 같은 함수를 사용하면 요구되는 매개변수 자료형을 맞추지 않을 경우 IDE가 대부분 경고를 해주기 때문에 이를 항상 맞춰 넣었다. 그래서 다른 자료형의 데이터를 넣었을 때 어떻게 동작할지는 거의 생각해보지 않았는데 지금은 프로그램 개발이 아닌 해킹을 하고있는 것이기 때문에 이렇게 함수를 일부러 고장내보면서 반환값이나 어떤 행동을 하는지 관찰해보는것도 좋은 습관이 될 것 같다. 원래는 문제를 풀 때 일체 검색같은걸 하지 않고 지금 내 머리에 있는 지식으로만 풀려고 했지만 이걸 풀면서 공부를 하려는 건데 미련하게 끙끙대고 있는 건 바보같다고 생각해서 이제부터는 직접적으로 풀이를 검색하진 않고 관련 키워드를 검색하며 공부하는 방식으로 풀어보려고 한다.

 

wargame.kr의 자체 튜토리얼 영상에도 이 문제의 핵심을 다루는 강의가 있다. 한 번 보면 좋을 듯.

 

'챌린지 > Wargame.kr' 카테고리의 다른 글

bughela - DB is really GOOD  (0) 2020.12.22
bughela - md5 password  (0) 2020.12.22
bughela - fly me to the moon  (0) 2020.12.16
bughela - WTF_CODE  (0) 2020.12.14
bughela - login filtering  (0) 2020.12.11