해당 문제는 대회 기간중엔 못풀고 롸업을 봤는데 트릭이 상당히 재미있어서 정리해 봤다.
먼저 문제에 들어가보면 아래와 같이 소스코드를 제공해 준다.
<?php
if (isset($_FILES['zip']) && $_FILES['zip']['size'] < 10*1024 ){
$d = 'files/' . bin2hex(random_bytes(32));
mkdir($d) || die('mkdir');
chdir($d) || die('chdir');
$zip = new ZipArchive();
if ($zip->open($_FILES['zip']['tmp_name']) === TRUE) {
for ($i = 0; $i < $zip->numFiles; $i++) {
if(preg_match('/^[a-z]+$/', $zip->getNameIndex($i)) !== 1){
die(':/ security');
}
}
exec('unzip ' . escapeshellarg($_FILES['zip']['tmp_name']));
echo $d;
}
}
else {
highlight_file(__FILE__);
}
코드를 보면 파일 업로드 시 특정 디렉토리 경로를 생성하고 해당 경로에 zip파일을 unzip하여 압축된 파일들이 업로드 되는 형태이다.
이 때 압축된 파일명들에 대해 정규식을 통해 검증하는데 알파벳 소문자외에 값이 들어갈 수가 없다. 즉 일반적인 형태로는 웹쉘 업로드가 불가능한 형태였다.
여기서 일단 커멘드 인젝션 같은 경우 escapeshellarg 함수가 적용되어있고 인풋으로 들어가는 값이 임시파일명이라 내가 컨트롤할 수 없는 부분이라 취약점을 터트릴 수 가 없었다.
그래서 일단 생각한게 Symlink 트릭으로 내부 파일 읽어오려고 해봤더니 업로드된 파일에 링크만 걸려있으면 무조건 권한부족으로 403이 떴다.
여기서 한참 헤매면서 ZipArchive관련 취약점들을 찾으면서 삽질을 했다.
대회 종료 후 롸업을 보니 취약점이 $zip->numFiles 부분에서 터지고 있었다.
위의 파일 검증 루틴을 보면 ZipArchive 객체의 numFiles 속성을 통해 Zip 파일 내 압축되어있는 파일 개수를 가져오는 데 이 값을 가져올 때 아래와 같이 Zip 파일 내 해당 헤더 영역의 값을 참조하게 된다.
저 값을 01에서 00으로 수정할 경우 $zip->numFiles 가 0으로 세팅되면서 반복문 내 파일명 검증 루틴을 Bypass할 수 있다.
그 후 unzip을 통해 헤더 값이 변조된 zip파일이 압축해제 될 때 unzip 명령어에서는 저 값과 상관없이 정상적으로 zip 내 파일들을 압축해제하게 되어 웹쉘 업로드가 가능해 진다.
위 형태에 맞춰서 php 확장자를 가진 파일을 압축한 다음 헤더 값을 변조하여 업로드하니 웹쉘 실행이 됐다.
Flag = hxp{please_ask_gynvael_for_more_details_on_zips_:>}
'CTF > Writeup' 카테고리의 다른 글
Codegate 2014 clone technique (1) | 2018.12.29 |
---|---|
X-MAS CTF 2018 Web Write up (0) | 2018.12.24 |
TenDollar CTF 2018 Cat-Proxy (0) | 2018.12.02 |
TenDollar CTF 2018 Kou (0) | 2018.12.01 |
TenDollar CTF 2018 XSS (0) | 2018.12.01 |