목차
♪ a 태그로 파일 다운로드
- 기존 구현 방식
- 기존 방식의 특징
- a 태그 적용 방식
- 정리
a태그로 파일 다운로드 구현
▷기존 파일 다운로드 구현 방식
다운로드 버튼 누르면 게시글의 id를 download.php에 전달 |
<?php
$idx = intval($_GET['id']);
$db_conn = mysqli_connection();
$sql = "SELECT * FROM upload_table WHERE idx = $idx";
$result = mysqli_query($db_conn,$sql);
$row = mysqli_fetch_array($result);
if(!$row) {
die('파일 정보를 찾을 수 없습니다.');
}
$image_path = $row['image_url'];
$name = explode("/",$image_path);
$file_name = end($name);
$file_size = filesize($image_path);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo,$image_path);
finfo_close($finfo);
if (file_exists($image_path)){ //파일 다운로드 지시를 위한 헤더
header("Content-Type:{$mime}");
header("Content-Disposition:attachment;filename={$file_name}");
header("Content-Length:{$file_size}");
header("Cashe-Control:cache,must-revalidate");
header("Pragma:no-cache");
header("Expires:0");
$fp = fopen($image_path,"r");
while(!feof($fp)) {
$buf = fread($fp,$file_size);
$read = strlen($buf);
print($buf);
flush();
}
fclose($fp);
} else {
die("파일이 존재하지 않습니다.");
}
?>
다운로드 프로세스 진행 |
▷기존 방식의 특징
- id 데이터에 intval 함수 사용으로 SQLi 위험도 낮춤
- 악성파일 업로드 취약점 + XSS 이용해서 강제 다운로드(CSRF) 시킬 수 있다고 생각했는데 어찌된 영문인지 이미지 파일을 불러오고 있음. 이미지 태그에 넣어서 그런 듯함. (브라우저는 이미지 태그를 다운로드하도록 하지 않음)
<img src="http://__ip__/download.php?id=20">
(그러고보니 파일 읽기 페이지로 img 소스 불러오기가 이런 식이었음.)
- 만약 SQL Injection에 대한 대비가 없다면 파일 다운로드 공격으로 서버 소스코드 다운로드 가능
<강제 다운로드시키는 법>
1. 스크립트로 다운로드 페이지 강제 리다이렉션
2. iframe으로 다운로드 페이지 불러오기
어차피 브라우저에서 다운로드했다고 알려주긴 하지만...
그래도 자동+페이지 변화없이 다운로드시키기엔 2,3번 방법이 적절한 듯함.
**근데 모든 파일 시그니처를 png로 하고 올리기도 하고 download.php에서 MIME타입을 지정해서 그런지 png파일로 다운받아짐.. 그래서 내부에 서버 측 스크립트를 작성해도 실행되지 않은 채 다운로드됨. 소스코드를 받을 수 없다면 어떻게 악용할 수 있을까 생각하다가 떠오른 게.. ↓
≫ 실행파일을 올려볼까?
실습 파일 |
파일 업로드 (이때 확장자 및 파일 시그니처 검사는 생략함) |
다른 게시글의 내용 위치에 강제 다운로드 스크립트 작성 후 수정 |
게시글 읽기 페이지 접근하여 실행파일 강제 다운로드 |
실행 성공 |
(파일 다운로드 공격..이라기보다 파일 업로드 + CSRF (+ XSS) 짬뽕인 것 같음)
▷a태그로 파일 다운로드 구현
//현재 코드
<a href="<?=$row['image_url']?>" download>다운로드</a>
- DB에서 이미지 저장 경로 불러와서 href 속성에 넣음
- download 속성 추가해서 해당 이미지 다운로드 가능 (인터넷 익스플로러 브라우저에서는 구현 못한다는 듯)
- download 속성은 SOP의 영향을 받아서 외부 파일은 다운받지 못하게 하는 듯함.
브라우저 화면 |
html 태그 |
- 저 조잡한 파란 글자를 누르면 이미지 파일을 다운받을 수 있음.
- href 속성을 수정해서 어디까지 할 수 있을까.
>>소스코드 요청
SQLi로 서버가 download.php 파일을 넣게 만듦 |
다운받은 파일을 열었을 때 |
SQLi로 서버가 login_case5.php (로그인 처리) 파일을 넣게 만듦 |
다운로드한 파일을 열어보니 login_case5.php 파일이 아니라 login.php 파일이 나옴 이번에도 php 코드는 보이지 않음 |
요청 기록 |
※ 모든 요청을 intercept하여 다시 확인한 결과 ※
id 파라미터에 SQLi를 시도하여 파라미터 수정 후 요청 전송
→ post.php 요청 전송되고 download.php?id=1 요청도 전송됨
→ 다운로드 버튼 클릭
→ login_case5.php 요청 전송 (여기서 login.php로 리다이렉션)
→ login.php 요청 전송
→ login.php 파일 다운로드 됨
Q1. | post.php 파일을 파라미터 수정 없이 요청하면 download.php?id=1 요청이 안 생김 SQLi을 통한 파라미터 수정과 download.php는 무슨 연관이 있길래 갑자기 생긴 거지? |
A1. | 내가 지금 게시글 읽기 페이지에 출력되는 이미지 파일을 download.php로 불러오고 있음. (<img src="/download.php?id=어쩌구">) 근데 이때 union select 1,2,...8 limit 1,1의 "1"이 download.php?id 파라미터 위치여서 download.php?id=1을 요청한 거임. 그러면 SQLi 없이 정상적으로 요청할 땐 download.php를 왜 요청하지 않느냐! 할 수 있음. 이해함! 버프스위트 http history에 안 떴으니까!! 근데 intercept 켜고 새로고침하면 post.php?id=47 이거 보려고 할 때 download.php?id=47도 뜸! 그냥 내가 버프스위트 설정을 그렇게 했던 거임!!! 젠장 download.php?id=47은 png 형식이지만 download.php?id=1은 html 형식이어서 필터링되지 않은 거였음!! |
Q2. | login_case5.php를 다운받으려고 했지만 리다이렉션된 페이지인 login.php 페이지가 다운받아짐 왜 다운로드 기능에 리다이렉션이 적용되는 거지? |
A2. | a태그 다운로드는 브라우저가 처리하는 방식이라 그런 듯함. html 페이지를 구성할 때 브라우저가 서버로 해당 파일(login_case5.php) 요청. 서버에서 login_case5.php 파일 요청 받고 코드 처리 후 전달하는데 이때 로그인 데이터가 없어서 login.php 페이지로 리다이렉션 시켜버린 듯함. 결국 브라우저에 전달된 파일은 login.php 파일이고 다운로드되는 것도 그 파일. |
이런 고로 소스코드를 받긴 힘들 듯함.
그럼 쉘 명령어를 파일에 넣어서 요청하면 실행된 결과를 알 수 있지 않을까?
>>서버 측 스크립트 파일 업로드 후 다운로드
/etc/passwd 파일을 읽는 명령어 삽입 후 업로드 |
파일 다운로드 |
파일 열었더니 /etc/passwd 파일 내용을 볼 수 있었음 |
소스코드도 볼 수 있을까?
소스코드 출력시키는 명령어를 삽입하고 업로드 |
다운받아서 확인했더니 소스코드 안 나옴 아 현재 업로드되는 디렉토리가 upload.php보다 하위 디렉토리라서 그런가? |
코드 수정 후 업로드 |
다운로드 받아서 읽어보니 소스코드 출력되는 중 디렉토리 위치가 문제였던 거임 |
지금은 다운로드를 파일 저장 경로를 통해 하는 중 서버 측 스크립트 업로드 가능 + 저장 경로 앎 -> 파일 업로드 조건 충족 이건 그냥 웹쉘도 가능한 경우였음... |
만약 download.php를 통해 다운받게 하면 그래도 명령어가 실행돼서 저장될까? |
내 생각엔 안될 것 같음. download.php로 받으면 png 형식으로 저장되어서 서버 측 스크립트를 실행할 거 같지가 않음.
<a href="download.php?id=<?=$row['idx'];?>">다운로드</a>
위와 같이 소스코드를 수정하고 동일한 게시글의 파일을 받았을 때 |
역시나 png 파일로 저장되고 있음 저걸 php 파일로 변경한 뒤 파일을 열어봄 |
코드는 실행되지 않았음 |
응답을 intercept해서 MIME 타입 text/html로 수정해도 저장만 phtml로 될 뿐 스크립트가 실행되지 않음 당연함 파일은 이미 서버의 손을 떠난 상태이기 때문임..... |
요청과 응답 사이에서 내가 어떻게 조작할 틈이 없어 보임..
그래서 SQLi를 활용해봄
현재 DB에서 idx값을 가져와 download.php의 파라미터로 사용하고 있기 때문에 파라미터에 SQLi를 시도함
idx값이 출력되는 1번 컬럼에 a 태그 스크립트를 삽입하여 다운로드되는 파일을 변경 |
다운로드한 파일을 읽어봤지만 역시나 소스코드는 출력되지 않음 대신 아까처럼 이미지 저장 경로로 삽입해봄 |
idx 출력 위치에 a태그와 서버 측 스크립트가 포함된 업로드 파일의 저장 경로를 추가 |
다운로드하여 읽어보니 역시나 명령어 실행 결과가 출력되고 있음 |
저장 경로를 알아야 가능한 방법임.. 근데 이것도 그냥 악성파일 업로드 취약점이라 웹쉘 올려서 요청하면 끝나는 겨.. |
≫ download.php가 id값이 아닌 파일 경로 값을 받는다면
<?php
$image_path = $_GET['file']; //file경로 GET파라미터로 전달
$name = explode("/",$image_path);
$file_name = end($name);
$file_size = filesize($image_path);
?>
이거 완전 내 세상
다 받을 수 있음 |
파일 경로로 하더라도 저렇게 정직하게 표현하는 곳이 있을까 싶긴 하지만..
정리
1. 다운로드 방식
ㄱ) 별도의 파일 읽기/다운로드 페이지 이용 (download.php)
ㄴ) a 태그 download 속성
2. 공격 시나리오
ㄱ) 소스코드 다운로드
ㄴ) 서버 측 스크립트 실행 (악성파일 업로드)
3. download.php?id=10 방식
-> 서버 측 스크립트 업로드하고 해당 파일 다운로드 시도
-> 기본적으로 소스코드 단순 읽기(실행x) 후 다운로드
장점 | 파일 저장 경로 숨길 수 있음 (-> 파일 업로드 공격 어느정도 대응 가능) 서버에서 접근 권한 통제할 수 있음 |
단점 | SQLi 대비하지 않으면 소스코드 털림 |
4. a 태그 방식
-> 서버 측 스크립트 업로드하고 해당 파일 다운로드 시도
-> 기본적으로 소스코드 실행 후 다운로드
장점 | 사용자가 업로드한 파일의 저장경로 노출 안되면 소스코드 숨길 수 있음 |
단점 | 저장경로 노출되면 웹쉘 등 서버 측 스크립트 실행 결과 받을 수 있는데 그럴 거면 아예 파일 경로 URL로 요청하는 게 편리 다운로드 관련 인증/인가 쪽 통제하기 어렵다..? |
5. a태그랑 download.php 결합
<a href="download.php?id=10" download>다운로드</a>
- 저장경로 숨길 수 있음
- 만약 download.php에 sqli가 있어도 소스코드는 이미 실행된 뒤에 다운로드됨.
- download.php에서 다운로드나 파일 접근 관련 인증/인가 통제 코드 추가하기 편함.
- ***근본적인 대응은 아님
'자습' 카테고리의 다른 글
virtualbox, 와이파이, 브릿지 모드, 호스트머신에서 보낸 요청이 와이어샤크에 캡처되지 않음 (0) | 2025.04.03 |
---|---|
3일간 진행한 버그 헌팅 실습 훈련 후기 (0) | 2025.02.15 |
CTF: SQL Injection 포인트 찾기 (0) | 2025.01.08 |
CTF: 어드민은 내 것이다. (0) | 2024.11.23 |