Description

Category: Web

Source: wargame.kr

Points: 247

Author: Jisoon Park(js00n.park)

Description:

JUST COMPARE ONLY.

with the other value :D

Write-up

비교만 한다고 한다. 문제 코드가 주어지니 조금 더 살펴보도록 하자.

<?php
    if (isset($_GET['view-source'])) {
         show_source(__FILE__);
         exit();
    }

    if (isset($_GET['v1']) && isset($_GET['v2'])) {
        sleep(3); // anti brute force

        $chk = true;
        $v1 = $_GET['v1'];
        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$chk = false;}
        if (!is_numeric($v2) ) {$chk = false;}
        if (md5($v1) != md5($v2)) {$chk = false;}

        if ($chk){
            include("../lib.php");
            echo "Congratulations! FLAG is : ".auth_code("md5_compare");
        } else {
            echo "Wrong...";
        }
    }
?>
<br />
<form method="GET">
    VALUE 1 : <input type="text" name="v1" /><br />
    VALUE 2 : <input type="text" name="v2" /><br />
    <input type="submit" value="chk" />
</form>
<br />
<a href="?view-source">view-source</a>

알파벳으로만 이루어진 v1과 숫자로만 이루어진 v2를 찾아야 하는데, 둘의 md5 hash가 같으면 된다고 한다.

MD5가 취약한 것으로 소문나있긴 하지만 임의의 충돌쌍을 그렇게 쉽게 찾을 수 있을 것 같지는 않다. 소스 코드를 좀 더 유심히 살펴보면, hash의 비교 연산자가 !==가 아닌 !=임을 알 수 있다.

type collision을 이용한 문제로 생각된다. type collision을 활용하려면, digest가 "0e"로 시작하고, 이후의 값들은 모두 숫자로 이루어져 있으면 된다.

간단히 코드를 작성하여 찾으려고 해보았으나, 생각보다 시간이 오래 걸려 검색을 통해 알아보았다. 검색창에 md5 0e까지만 입력을 하자 md5 beggining with 0e라는 추천 검색어가 보였고(ㅎㅎㅎ), 이를 이용해서 원했던 입력값들인 240610708QNKCDZO를 쉽게 찾을 수 있었다.

찾아낸 값들을 넣으면 flag를 얻을 수 있다.

Flag : 242af79b3c0ce74de3ed7769f9f781374a52a063

'writeups > Web' 카테고리의 다른 글

php? c?  (0) 2019.11.25
md5 password  (0) 2019.11.25
login with crypto! but..  (0) 2019.11.25
login filtering  (0) 2019.11.25
fly me to the moon  (0) 2019.11.25

Description

Category: Web

Source: wargame.kr

Points: 448

Author: Jisoon Park(js00n.park)

Description:

sucker_enc is sucks.

Can you login?

Write-up

id, ssn, password를 이용한 로그인 시스템이다. 소스 코드를 살펴보면 id와 password는 그대로 DB에 저장하고, ssn은 sucker_enc()라는 함수를 이용해 암호화 하여 저장한다.

(... 생략 ...)

function enc($str){
 $s_key = "L0V3LySH:butsheismyxgf..";
 $s_vector_iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB), MCRYPT_RAND);
 $en_str = mcrypt_encrypt(MCRYPT_3DES, $s_key, $str, MCRYPT_MODE_ECB, $s_vector_iv);
 $en_base64 = base64_encode($en_str);
 $en_hex = bin2hex($en_str);
 return $en_hex;
}

function sucker_enc($str){
 for($i=0;$i<8;$i++) $str = enc($str);
 return $str;
}

function get_password($user,$ssn){
 db_conn();
 $user = mysql_real_escape_string($user);
 $ssn  = mysql_real_escape_string($ssn);
 $result = mysql_query("select user_ps from accounts where user_id='{$user}' and encrypt_ss='".sucker_enc($ssn)."'");
 $row = mysql_fetch_array($result);
 if ($row === false) {
  die("there is not valid account!");
 }
 return $row[0]; 
}

ini_set("display_errors", true);

if( (isset($_POST['user']) && isset($_POST['ssn']) && isset($_POST['pass'])) ){
 
 sleep(2); // do not bruteforce !!!! this challenge is not for bruteforce!!

 if($_POST['pass'] == get_password($_POST['user'],$_POST['ssn'])){

  if($_POST['user'] == "admin"){
   echo "Login Success!!! PASSWORD IS : <b>".auth_code("login with crypto! but..")."</b>";
  }else{
   echo "Login Success. but you r not 'admin'..";
  }
 }else{
  echo "Login Failed";
 }

}

소스코드에 admin의 ssn이 나와 있긴 하지만 password와 ssn을 모두 알 수 없다면 로그인은 어려워 보인다.

get_password() 함수에서 sucker_enc() 함수의 결과값을 내 마음대로 바꿀 수 있다면 sql injection이 가능할 것 같아 sucker_enc()와 enc() 함수를 좀 더 살펴보았지만, sucker_enc() 함수는 hex string을 돌려주기 때문에 이를 이용한 sql injection은 어려울 것 같았다.

sucker_enc() 함수를 좀 더 살펴보다가 DB에 들어가는 결과값을 보았는데 '881114'에 대한 암호화 결과가 2048 바이트였다. 좀 더 긴 문자열을 암호화 해보았더니 대충 256배로 길이가 늘어나는것 같았다.

혹시나 해서 php의 string형 데이터에 길이 제한이 있는지 살펴봤더니 다음과 같은 사실을 알 수 있었다.

  • php 7.0.0 이상(64bit)에서는 문자열 최대 길이에 제한이 없음
  • php 5.x 이상(32bit)에서는 문자열 최대 길이가 최대 2GB(길이가 signed int로 저장됨)
  • 최대 길이가 그렇고, 실제로는 php.ini에 정의된 memory_limit 값에 의해 정의되는데, PHP 5.2에서는 default로 128MB이고, 그 이전 버전에서는 8MB였음

256배로 길이가 늘어났었으니 일단 기본값 128MB의 1/256인 524,288 byte 입력을 넣어서 어떻게 되나 확인해 보았다.

에러가 발생한다! ㅎㅎㅎㅎ 코드에 보면 ini_set() 함수를 이용해서 에레 메세지를 돌려주도록 되어있는 덕분인것 같다. 제대로 가고 있다는 이야기다.

입력 길이를 반씩 줄여가면서 에러가 발생하지 않을때까지 반복해 보았다. 262,144 길이의 입력에서는 동일하게 에러가 발생하였고, 131,072 길이의 입력을 넣었더니 에러가 warning으로 바뀌었다.

에러가 발생한 것은 줄번호 39라인의 bin2hex() 구문이었는데, warning은 52라인에서의 query 실패와 53라인에서 mysql_fetch_array()에 boolean이 들어왔다는 내용이었다. (어쨌든 결과는 Login Failed)

코드를 다시 보면, mysql_fetch_array() 함수에서 문제가 생겨서 FALSE가 리턴된 후 mysql_fetch_array() 함수에 FALSE가 들어간 것으로 생각된다. 발생한게 warning이고 "Login Failed" 메세지까지 나온걸 보면 일단 계속 진행은 되었던 것 같다.

mysql_fetch_array(FALSE)는 NULL을 리턴했을 거고, NULL === FALSE는 FALSE이니 "there is not valid account!" 메세지가 나오지 않은 것이 이해가 간다.

그럼, get_password() 함수는 최종적으로 NULL을 리턴할 것이다. (확신이 안가서 php 테스트 코드를 만들어 테스트 해보았다.)

get_password() 함수의 결과값을 pass와 비교하고 있으니, pass를 NULL로 만들어서 flag를 획득할 수 있었다.

import requests

'''
proxies = {
  'http': 'http://127.0.0.1:8080',
  'https': 'http://127.0.0.1:8080',
}
'''

URL = 'http://wargame.kr:8080/login_with_crypto_but/index.php'
data = {'user' : 'admin', 'ssn' : '0' * 131072, 'pass' : ''}

response = requests.post(URL, data = data) #, proxies = proxies)
print response.text

Flag : 5ffa466ab40ecdc6d5ea01ea04a3b52c9cd15986

'writeups > Web' 카테고리의 다른 글

md5 password  (0) 2019.11.25
md5_compare  (0) 2019.11.25
login filtering  (0) 2019.11.25
fly me to the moon  (0) 2019.11.25
Calculator  (0) 2019.11.23

Description

Category: Web

Source: wargame.kr

Points: 141

Author: Jisoon Park(js00n.park)

Description:

I have accounts. but, it's blocked.

can you login bypass filtering?

Write-up

ID와 PWD를 입력해서 login을 할 수 있고, 문제의 소스가 제공된다.

소스 코드를 살펴보자.

<?php

if (isset($_GET['view-source'])) {
    show_source(__FILE__);
    exit();
}

/*
create table user(
 idx int auto_increment primary key,
 id char(32),
 ps char(32)
);
*/

 if(isset($_POST['id']) && isset($_POST['ps'])){
  include("../lib.php"); # include for auth_code function.

  mysql_connect("localhost","login_filtering","login_filtering_pz");
  mysql_select_db ("login_filtering");
  mysql_query("set names utf8");

  $key = auth_code("login filtering");

  $id = mysql_real_escape_string(trim($_POST['id']));
  $ps = mysql_real_escape_string(trim($_POST['ps']));

  $row=mysql_fetch_array(mysql_query("select * from user where id='$id' and ps=md5('$ps')"));

  if(isset($row['id'])){
   if($id=='guest' || $id=='blueh4g'){
    echo "your account is blocked";
   }else{
    echo "login ok"."<br />";
    echo "Password : ".$key;
   }
  }else{
   echo "wrong..";
  }
 }
?>
<!DOCTYPE html>
<style>
 * {margin:0; padding:0;}
 body {background-color:#ddd;}
 #mdiv {width:200px; text-align:center; margin:50px auto;}
 input[type=text],input[type=[password] {width:100px;}
 td {text-align:center;}
</style>
<body>
<form method="post" action="./">
<div id="mdiv">
<table>
<tr><td>ID</td><td><input type="text" name="id" /></td></tr>
<tr><td>PW</td><td><input type="password" name="ps" /></td></tr>
<tr><td colspan="2"><input type="submit" value="login" /></td></tr>
</table>
 <div><a href='?view-source'>get source</a></div>
</form>
</div>
</body>
<!--

you have blocked accounts.

guest / guest
blueh4g / blueh4g1234ps

-->

mysql_fetch_array를 하기 전에, mysql_real_escape_string() 함수를 이용해서 특수문자를 처리하도록 하고 있다.

mysql_real_escape_string() 함수를 우회할 수 있는 방법을 찾아보면, multibyte encoding을 사용해서 우회할 수 있다고 나오는데, 열심히 삽질해 보았지만 문제를 풀 수 없었다. multibyte 안쓰는걸로...

코드를 다시 자세히 보면, if문에서 $row에 id가 있을 경우, id가 guest 또는 blueh4g인지 확인하고 있는데, $row['id']=='guest'로 쓰는게 아니라, $id와 비교하고 있는 것을 알 수 있다.

알다시피, php에서는 문자열에 대/소문자를 구분한다. 그렇지만, 간과하기 쉬운데, sql query에서는 그렇지 않다. (select ...으로 쓰던 SELECT ...로 쓰던 동일하게 동작한다.)

그러니, php가 다른 문자열이라고 판단하도록 'Guest' 등의 id로 로그인을 시도하면 DB query는 작동하고 if문은 회피할 수 있다.

(솔직히 나는 이걸 생각해내지 못해서 다른 사람의 풀이를 찾아봤다 ㅜㅠ)

ID/PWD 넣는 텍스트박스가 나오면 무조건 SQL INJECTION이라고 생각했었는데, 그런면에서 보면 신선한 문제였다. 왠지 머리가 부드러운(?) 젊고 어린 해커들은 1초만에 풀었을거 같다...

Flag : 63222f28790ea16a173b7a7bc0f1ddcbd73bd08b

'writeups > Web' 카테고리의 다른 글

md5_compare  (0) 2019.11.25
login with crypto! but..  (0) 2019.11.25
fly me to the moon  (0) 2019.11.25
Calculator  (0) 2019.11.23
WTF_CODE  (0) 2019.11.23

Description

Category: Web

Source: wargame.kr

Points: 200

Author: Jisoon Park(js00n.park)

Description:

javascript game.

can you clear with bypass prevent cheating system?

Write-up

간단한(?) 웹게임 해킹 문제이다.

좌우로 움직이는 벽을 피하는 게임인데, 한번 죽어보면 31337점을 획득해야 한다는 메세지가 뜬다.

게임 코드는 난독화 되어있는데, 연습 겸 분석해보자.

코드는 eval() 함수를 이용하도록 되어있다. eval() 함수에 주어지는 입력을 확인하기 위해서, eval을 document.write로 수정한 후 chrome의 개발자 도구에서 실행시켜 보면 일련의 소스 코드를 얻을 수 있다.

아직 좀 복잡하게 생겼으니, 적당한 beautifier를 찾아서 한번 더 정리해주면 좀 예쁜 코드를 볼 수 있다.

function secureGame() {
    var _0x8618x2 = this;
    var _0x8618x3 = true;

    /************/
    /* 중간 생략 */
    /************/

        if (BTunnelGame['checkLife']()) {
            setTimeout('updateTunnel()', 10)
        } else {
            $('img#ship')['fadeOut']('slow');
            $('img.left_wall')['css']('display', 'none');
            $('img.right_wall')['css']('display', 'none');
            $['ajax']({
                type: 'POST',
                url: 'high-scores.php',
                data: 'token=' + token + '&score=' + BTunnelGame['getScore'](),
                success: function(_0x8618x19) {
                    showHighScores(_0x8618x19)
                }
            })
        }
    };

    /************/
    /* 중간 생략 */
    /************/

    var token = '';

    function updateToken() {
        $['get']('token.php', function(_0x8618x20) {
            token = _0x8618x20
        })
    };

코드를 보면 checklife 해서 token과 score를 보내는 곳이 있고, token을 업데이트 하는 곳이 있다. token은 token.php를 이용해서 otp 형식으로 업데이트 되는것 같다. (burp suite에서도 확인할 수 있다.)

burp suite의 repeater를 이용해서 token을 다시 받아오고, 이 token과 함께 31337이라는 score를 보내 보면 "TOKEN ERROR - by. cheating prevention system"라는 에러 메세지를 만날 수 있다.

실제 게임이 동작하는 와중에 보내야 될 것 같은데, 더 분석하기는 귀찮으니 burp suite의 Match and Replace 기능을 이용해보자.

request body에 score=?를 만족하는 문자열이 보이면 점수를 바꿔서 보내도록 해두었다.

다시 게임을 시작한 후, 적당히 빨리 죽어주면 flag를 얻을 수 있었다.

Flag : 13d93b28b53d08b1bd9f43c497b390c7b9593943

'writeups > Web' 카테고리의 다른 글

login with crypto! but..  (0) 2019.11.25
login filtering  (0) 2019.11.25
Calculator  (0) 2019.11.23
WTF_CODE  (0) 2019.11.23
webhacking.kr 058  (0) 2019.11.23

Description

Category: Forensic

Source: HDCON 2017 Quals.(September)

Points: 200

Author: Jisoon Park(js00n.park)

Description:

랜섬웨어를 분석하여 암호화된 문서를 복호화하고 플래그를 찾으시오.

!!!!리얼머신이 아닌 가상머신에서 분석해야 하며 실행시키면 안됩니다. !!!!

!!!!두번 실행시키면 복구가 불가능하오니 이점 양해해주십시오.!!!!!

Write-up

문제 파일을 열어서 복호화 해보면, 최종적으로 decrypt 해야 할 파일과 악성코드로 보이는 실행파일, 그리고 덤프파일(아마 메모리 덤프)이 존재한다.

암호화된 파일은 지금 열어봐야 아무런 도움이 안될테니, 실행파일과 덤프 파일 중에서 우선 덤프 파일을 확인해 보자.
240MB가 넘는 큰 파일이 때문에, 한땀한땀 보기 보다는 strings 명령어를 이용해서 10글자 이상의 문자열들만 먼저 찾아본다.

$ strings -n 10 wanna_samile.exe.dmp > wanna_smile.exe.dmp.strings

찾아진 문자열들을 확인해나가다 보면... 응? Public Key가 보인다.

밑져야 본전이니 PRIVATE 문자열을 검색해보자.

맙소사.. 일단 복사해서 소중히 간직해 두기로 한다. 개인적인 생각이지만, 출제자가 일부러 넣어둔게 아닌것 같다는 생각을 했다. search 해보면 알겠지만, 이 부분 말고 Private Key가 일부만 들어있는 문자열도 있다. 원래는 그쪽에서 Private Key를 복구해내야 하는 문제가 아니었을까... 어쨌든, 원래 400점이었던 이 문제의 배점이 대회 중에 200점으로 쪼그라들었다. ㅋ

Key Pair 확보로 든든해진 마음을 갖고 주어진 실행파일을 (실행은 하지 말라고 했으니) ida로 분석해본다.

찬찬히 뜯어보다 보면, 파일에 대한 암호화를 실행하는 sub_402960() 함수를 찾을 수 있다. 위아래로 이런저런 코드들이 있지만, 실제로 봐야 할 부분은 얼마 안된다. 다행히 코드를 꼬아놓거나 하진 않았다.

위 코드를 살펴보자. sub_401080() 함수는 256bit AES Key를 세팅하는 함수이고, sub_4010D0() 함수는 AES Key를 이용해서 원래의 파일을 암호화 하는 함수이다. sub_402220() 함수에서 AES Key를 RSA로 암호화 하고, sub_4028C0() 함수에서 최종적으로 암호화된 파일을 완성한다.

sub_2028C0() 함수를 조금 더 살펴보면, decryptme.docx.smile 파일의 헤더 구조를 확인할 수 있다. 아래와 같이, "WANASML!" 문자열로 시작하고 12번째 바이트에서부터 암호화된 AES Key가 기록되며, 272번째 바이트에서 원래 파일의 길이가 기록된다.

AES Key를 복호화해내기 위한 RSA Private Key는 알고 있으니, decryptme.docx.smile 파일로부터 암호화된 AES Key 부분을 조심스럽게 추출해낸다.

별도의 python 스크립트, Java 또는 C 코드를 작성해도 무방하나, 간단히 사용할 수 있는 openssl command line tool을 사용해보자.

별다른 에러 메세지가 없는 걸로 봐서 성공적으로 복호화가 된것 같은데, 복호화된 파일의 크기가 245바이트이다. 정상적인 AES 256 Key라면 32 바이트이어야 할텐데, 크기가 이상하니, ida에서 AES Key를 암호화 하는 부분을 잠깐 살펴본다.

입력받은 AES Key의 길이는 32 바이트가 맞는데, 실제로 RSA_public_encrypt() 함수를 호출하면서 암호화 할 길이(v8)로 245 바이트를 지정하고 있다. 이걸로 미루어, RSA 복호화는 정상적으로 수행되었고, 복호화 된 245 바이트 중 첫 32 바이트가 AES Key임을 알 수 있다.

openssl command line tool을 한번 더 사용하여 decryptme.docx.smile을 파일을 복호화 하자.(헤더 부분 제외)

헤더의 마지막에 0x6517 바이트로 원본의 길이가 기록되어 있었으니, 길이에 맞게 파일을 잘라주자. (잘라주지 않아도 큰 상관은 없다. 보통 파일 뒤의 부가 정보는 크게 문제가 되지 않으므로) 다만, AES Key를 암호화 할 때 PKCS 패딩을 한게 아니기 때문에, 그냥 복호화 하면 openssl이 에러를 출력한다. 그래서 -nopad 옵션을 주고 패딩을 계산하지 않도록 해야 정상적인 복호화가 가능하다.

파일 이름에도 docx라고 나와있었지만, 확인 결과 Microsoft Word 파일이므로 docx로 확장자를 변경하고 파일을 열어보면 flag를 획득할 수 있다.

Flag : TIMEISGOLD!

'writeups > Forensic' 카테고리의 다른 글

img recovery  (0) 2019.11.23

Description

Category: Reversing

Source: HDCON 2017 Quals.(Febuary)

Points: 100

Author: Jisoon Park(js00n.park)

Description:

가위바위보에 연속적으로 이겨서 플래그를 획득하시오

Write-up

apk 파일이 주어진 것으로 보아, android 리버싱 문제이다. 코드 분석에 앞서, Android 기기에 파일을 올려서 동작을 확인해 본다.

가위바위보를 하는 단순한 게임인데, Gole Score(Goal Score의 오타인듯)가 17916인걸로 봐서, 17916번을 연속으로 이기면 flag를 획득할 수 있는것으로 보인다.
brute force 같은건 생각하지 말고, 코드 분석으로 넘어가자.

apk 파일은 zip 파일과 같은 형태이므로, unzip을 이용해서 압축을 풀고, classes.dex 파일을 jar 파일로 변환한 뒤, jd-gui를 통해 코드 분석을 실시하면 된다.

$ unzip rock.apk
   ...
$ dj2-dex3jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
$ jd-gui classes-dex2jar.jar

열심히 뜯어볼 것도 없이, 친절하게 CallMe.class라는 클래스가 있다. 이 클래스가 호출되도록 하면 되는 것 같다. 이 클래스가 하는 일은 onReceive() 메소드에 기록되어 있다.

문자열을 갖고 조합하는 코드인 것 같은데, 한땀한땀 파악하긴 귀찮으니 그냥 그대로 C 코드로 옮긴다. ck라는 함수는 Native API이다. IDA에서 decompile 해서 코드로 옮기면 된다.

마지막으로, paramIntent에서 'mm'이라는 태그를 통해 넘어온 문자열을 찾아야 하는데, 가위바위보를 이겼을 경우 CallMe가 호출될 것이므로, MainActivity에서 이 부분을 찾아보면 Native 함수인 rps_calc() 함수의 반환 값이 전달되는 것을 알 수 있다.

rps_calc() 함수를 decompile 해서 살펴보자.
어떤 변수(k)가 이 게임의 목표 점수였던 17916보다 큰 값을 가질때 뭔가를 수행하는 부분을 찾을 수 있다.

이왕 코드로 옮기기로 한 것, 이 부분도 따와서 붙여넣도록 한다.

최종 코드는 아래와 같이 완성된다.

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

signed short CallMe_ck(signed int a3, signed short a4, signed short a5)
{
  signed short result; // ax@1

  result = a5;
  //LOBYTE(result) = a5;

  switch ( a3 % 6 )
  {
    case 0:
      result = ((a4 & 0xFFF0u) >> 4) ^ a5;
      goto LABEL_8;
    case 1:
      result = ((a4 & 0xFFE0u) >> 5) ^ a5;
      goto LABEL_8;
    case 2:
      result = ((a4 & 0xFF80u) >> 7) ^ a5;
      goto LABEL_8;
    case 3:
      result = ((a4 & 0xFFC0u) >> 6) ^ a5;
      goto LABEL_8;
    case 5:
      result = a5 ^ 0xF;
      goto LABEL_8;
    case 4:
LABEL_8:
      result = (char)result;
      break;
    default:
      result = 67;
      break;
  }
  return result;
}

void onReceive(char* v10)
{
  ///CallMe_onReceive///
  register int i;
  char* paramContext = v10;
  char arrayOfChar[] = "SBtbhfle_7tg]Runsj5]io_MBmi";
  char paramIntent[100] = {0};

  for (i = 0; i < strlen(arrayOfChar); i++)
  {
    if (i < 16) {
      paramIntent[i] = CallMe_ck(i, paramContext[i], arrayOfChar[i]);
    } else {
      paramIntent[i] = CallMe_ck(i, paramContext[i - 16], arrayOfChar[i]);
    }
  }

  printf("%s\n", paramIntent);

  return;
}

int main(void)
{
  ///rps_1calc///
  register size_t v6; // esi@4
  signed int v10; // [sp+8h] [bp-354h]@1
  signed int v11; // [sp+Ch] [bp-350h]@1
  signed int v12; // [sp+10h] [bp-34Ch]@1
  signed int v13; // [sp+14h] [bp-348h]@1
  char v14;

  v14 = 0;
  v13 = 658317629;
  v12 = 540877373;
  v11 = 1581080381;
  v10 = 0x533D5458;

  printf ("%08x %08x %08x %08x %08x\n", (unsigned int)&v10, (unsigned int)&v11, (unsigned int)&v12, (unsigned int)&v13, (unsigned int)&v14);

  if ( strlen((const char *)&v10) )
  {
    //v9 = 0x10;
    v10 = (v10 & (~0xff)) ^ (0x10 ^ 0x58);
    if ( strlen((const char *)&v10) >= 2 )
    {
      v6 = 1;
      do
        *((unsigned char *)&v10 + v6++) ^= 0x10;
      while ( v6 < strlen((const char *)&v10) );
    }
  }
  printf("%s\n", (char*)&v10);

  onReceive(&v10);

  return 0;
}

위 코드를 컴파일하여 실행하면 flag 값을 얻을 수 있다.

Flag : W@tching_7th_Sunse7_in_B@li

'writeups > Reversing' 카테고리의 다른 글

Local News  (0) 2019.11.25
KeyGenMe  (0) 2019.11.25
pyc decompile  (0) 2019.11.25
Easyhaskell  (0) 2019.11.25
EASY_CrackMe  (0) 2019.11.25

Description

Category: Reversing

Source: wargame.kr

Points: 353

Author: Jisoon Park(js00n.park)

Description:

bughela.pyc

:D

Write-up

서버의 시간과 pyc 파일이 주어진다.

pyc 파일은 쉽게 decompile 할 수 있는데, 나는 pycdc라는 decompiler를 이용해서 다음과 같은 소스를 얻었다.

import time
from sys import exit
from hashlib import sha512

def main():
    print 'import me :D'


def GIVE_ME_FLAG(flag):
    if flag[:43] != 'http://wargame.kr:8080/pyc_decompile/?flag=':
        die()
    flag = flag[43:]
    now = time.localtime(time.time())
    seed = time.strftime('%m/%d/HJEJSH', time.localtime())
    hs = sha512(seed).hexdigest()
    start = now.tm_hour % 3 + 1
    end = start * (now.tm_min % 30 + 10)
    ok = hs[start:end]
    if ok != flag:
        die()
    print 'GOOD!!!'


def die():
    print 'NOPE...'
    exit()

if __name__ == '__main__':
    main()

GIVE_ME_FLAG() 함수에 있는 내용을 복사해다가 'GOOD!!!'을 출력하는 flag를 만들면 될 것 같다.

import time
from sys import exit
from hashlib import sha512
import requests

def make_flag():
    now = time.localtime(time.time())
    seed = time.strftime('%m/%d/HJEJSH', time.localtime())
    hs = sha512(seed).hexdigest()
    start = now.tm_hour % 3 + 1
    end = start * (now.tm_min % 30 + 10)
    ok = hs[start:end]
    return ok

URL = 'http://wargame.kr:8080/pyc_decompile'
data = {'flag' : make_flag()}

response = requests.get(URL, params = data)
print response.text

이대로 실행해보면 어찌된 일인지 WRONG......이라는 메세지가 나온다. 이게 왜이러나 싶어 유심히 보아도 별달리 틀린 부분을 못찾았는데, 서버 시간이 주어졌던게 생각나서 설마 하고 로컬 타임과 비교해 보았다.

print "server time : " + response.headers['Date']
print "local time  : " + time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
server time : Fri, 21 Dec 2018 05:59:05 GMT
local time  : Fri, 21 Dec 2018 05:57:54 GMT

대충 1~2분 정도 빠르다. ㅡ_ㅡ;;

서버 시간을 받아서 파싱해도 되지만, 귀찮으니 11번 라인을 간단하게 고쳐서 사용했다.

    end = start * ((now.tm_min + 2) % 30 + 10)

Flag : f17b0303b74c1c37e3be86b9461716074707b1b5

'writeups > Reversing' 카테고리의 다른 글

KeyGenMe  (0) 2019.11.25
rock  (0) 2019.11.25
Easyhaskell  (0) 2019.11.25
EASY_CrackMe  (0) 2019.11.25
Crypto Crackme Basic  (0) 2019.11.25

Description

Category: Misc/Polynomial Solving

Source: wargame.kr

Points: 682

Description:

Can you find the solution quickly in polynomials?

nc wargame.kr 10004

Write-up

nc로 문제 서버에 접속해보면 아래와 같은 메세지를 받을 수 있다.

matta@JSPARK-SURFACE:~/matta/GDrive/current/wargame.kr/plz_variable$ nc wargame.kr 10004
Please match the correct answer 30 times.
Submit format -> a,b,c,d,,,(Ascending order)
Timeout = 60sec
a,b,c,d,,, is natural number
a,b,c,d,,, is 100 <=  <= 1000

1th...
b - c - a + d = -230
c * b + d * a = 388688
d + b - c + a = 378
d + c + a + b = 2122
Answer ->

60초 내에 30개의 1차 다항식 문제를 풀면 되는 문제이다. 몇번 시도해 보면 미지수의 갯수만큼 다항식이 주어지는 것을 알 수 있다.

이 다항식들과 미지수의 범위(100 <= x <= 1000)를 이용해서 각 미지수를 구해보자.

python polynomial로 검색을 해보면 sage, sympy, numpy, scipy 등 다항식 풀이를 지원하는 다양한 라이브러리들을 볼 수 있는데, sage와 sympy 등으로 문제를 풀려고 해보았지만 잘 되지 않았다.
(너무 오래 걸리거나 왠지 모르지만 값이 이상하게 나오거나..)

이런저런 검색 끝에 z3 solver를 찾았는데, 이거 좀 좋은거 같다 ㅋㅋㅋ

문제에서 원하는 바가 굉장히 명확하므로, exploit에 대한 설명으로 writeup을 대신한다.

for idx in range(30):
  print "====================="
  print r.recvuntil("th...\n")
  print "=====================\n"

  eqs = r.recvuntil("-> ")
  print eqs

  eqs = [l.replace("=", "==") for l in eqs.split("\n")[:-1]]

다항식은 n th...라는 메세지 이후로 나오므로, 이전의 메세지는 버리고 마지막 Answer -> 까지의 데이터의 각 라인에서 방정식을 얻는다. (마지막 Answer가 있는 라인은 버린다.)

  variables = list(set(re.findall(r"[a-z]","".join(eqs))))
  variables.sort()

방정식에서 등장하는 소문자 알페벳을 모아 (set을 이용하여) 중복을 제거하고 오름차순으로 정렬한다.

  solver = z3.Solver()

  for var in variables:
    exec("%c = z3.Int('%c')"%(var,var))
    solver.add(eval("%s >= 100"%var))
    solver.add(eval("%s <= 1000"%var))

  for eq in eqs:
    solver.add(eval(eq))

z3 solver를 선언하고, 미지수 선언과 동시에 각 미지수에 대한 범위 부등식을 solver에 추가한다. 그리고 나서 문제에서 주어진 각 다항식 또한 solver에 추가한다.

변수 선언을 자동으로 하기 위해 exec()를 사용하였고, solver는 다항식을 문자열로 받지 않기 때문에 eval()을 사용하였다.

이 부분에서 삽질을 꽤 했는데, z3의 미지수 변수와 for문의 반복 변수를 동일하게 쓰는 바람에 문제가 생겼었다.
(그래서 변수 이름이 평소와 달리 다 두글자 이상이다... 설마.. 그래서 문제 이름이?!)
exec()로 변수 선언을 할때는 이런 부분을 꼭 주의하도록 하자.

  #solve!
  solver.check()
  ans = solver.model()

solver에 필요한 식이 모두 추가되면 check() 메소드를 이용해 해를 구한다. model()을 이용하면 해(solution)를 저장하고 있는 model reference에 대한 instance를 얻을 수 있다.

  resp = []
  for var in variables:
    resp.append(str(ans.evaluate(eval(var)).as_long()))

  r.sendline(",".join(resp))

model reference는 range, int, boolean 등 다양한 형태의 해를 가질 수 있기 때문에, 적절한 방법을 통해 추출해야 한다.

여기서는 각 미지수의 값을 정수(long)로 가져와서 문자열 리스트의 형태로 저장한 후 문제 서버로 전송하였다.

for문에 따라 30번의 문제를 푼 결과 flag를 얻을 수 있었다.

Flag : wmkr{Wow_Fuck_the_z3?!}

'writeups > Coding|misc.' 카테고리의 다른 글

Who do I trust?  (0) 2019.11.25
SQL  (0) 2019.11.25
input  (0) 2019.11.25
algo-auth  (0) 2019.11.25
PWN  (0) 2019.11.23

+ Recent posts