File Upload 취약점이란?
File Upload 취약점이란 웹 서버에서 사용자가 업로드하는 파일의 이름, 유형, 크기 등을 충분히 검증하지 않고 서버 시스템에 업로드를 허용할 때 발생하는 취약점을 의미한다.
웹 서비스의 기능을 구현하면서 파일 업로드를 허용해야 하는 경우가 있다. 일반적으로 커뮤니티 기능을 구현하는 경우 게시판에 파일을 올리거나 프로필 사진을 등록하는 경우 등이 이에 해당한다. 이때 서버는 이후 사용자가 해당 파일을 요청할 때 사용자에게 파일을 보여줘야 하기 때문에 서버 내의 임의의 위치에 저장해두게 된다.
하지만 사용자가 파일을 업로드할 때 서버에서 파일을 검증하지 않는다면 사용자는 서버에 원래 목적과 다른 파일을 저장시킬 수 있을 것이다. 악성코드를 저장시켜 서버에 피해를 줄 수도 있고, 기존에는 존재하지 않는 페이지 파일을 만들거나 기존에 있는 페이지 코드를 다른 코드로 대체할 수도 있고, 만약 파일을 저장하는 위치까지 임의로 정할 수 있다면 서버 시스템 내의 다른 파일을 덮어씌워 파일을 망가뜨릴 수도 있을 것이다.
이처럼 서버에 임의의 파일을 업로드할 수 있는 취약점을 통칭 File Upload 취약점이라고 한다.
왜 위험할까?
애초에 File Upload 취약점이란 것이 공격자가 서버에 원하는 파일을 올릴 수 있는 취약점이다보니 이러한 행동이 위험할 것이라는 건 본능적으로 추측할 수 있다. 무슨 짓을 할 줄 알고. 그런데 이 ‘무슨 짓’이 정확히 어떤 짓인지도 알아야 할 필요가 있을 것이다. 얼마나 위험한 취약점인지도 알아야 경각심이 생기고 하니까.
File Upload 취약점으로 할 수 있는 공격의 종류는 너무나도 무궁무진하다. 공격자가 어떻게 활용하느냐에 따라 종류는 정말 다양하지만 가장 대표적인 예시를 몇 가지만 보이자면 다음과 같다.
1. 웹쉘 업로드
가장 치명적인 공격이다. 웹서버에 쉘 명령어를 실행시키는 php코드를 삽입하는 것이다. 이를 통칭 웹쉘(web shell)이라고 한다. 시스템의 쉘을 획득한다는 것은 시스템 운영체제에 직접 명령을 내릴 수 있다는 의미이고, 이는 시스템의 제어권을 획득한다는 것과 동일하기 때문에 아주 치명적인 결과를 가져온다. (여담으로, 이러한 이유로 시스템 해킹의 최종 목적은 시스템의 쉘을 획득하는 것이다.) 만약 *리버스 쉘까지 이어질 수 있다면 피해 규모는 측정 불가다.
*리버스 쉘 : 네트워크 상에서 쉘을 획득하는 방식의 일종이다. 바인드 쉘은 클라이언트가 서버에 접속해서 쉘을 획득하는 방식, 리버스 쉘은 이와 반대로 서버가 클라이언트에 접속하여 쉘을 넘겨주는 방식이다. 방화벽 문제로 일반적으로는 바인드 쉘보단 리버스 쉘을 사용한다.
2. Deface 공격
디페이스(Deface) 공격은 쉽게 말해 ‘나 여기 해킹했어요’ 라는걸 웹 페이지 메인화면에 박아넣는 공격방식이다. 무언가의 표면에 글을 쓰거나 그림을 그려 외관을 훼손한다는 deface라는 단어에서 가져온 의미이다. 보통 디페이스 공격은 해커가 해킹을 성공했다는 사실을 알려 과시하기 위한 목적의 공격이다.

이런 디페이스 공격이 파일 업로드 취약점에 의해 발생할 수도 있다. 웹 페이지를 표시한다는 것은, 브라우저가 웹 서버에 있는 웹페이지 파일을 가져와 브라우저에 표시하는 것이다. 이때 웹 서비스 내에 파일 업로드 취약점이 존재한다면 공격자는 메인 페이지(예를 들어 index.php)와 동일한 파일명으로 파일을 업로드할 수 있을 것이다. 이때 메인 페이지 파일을 덮어씌우게 되면 이후 메인 페이지를 요청했을 때 공격자가 업로드한 파일로 웹 페이지가 대체되는 것이다.
문제는 이러한 디페이스 공격은 해당 페이지 자체가 서버 내에서 사라지는 것이기 때문에 해당 페이지(서비스)는 이용 불가 상태가 된다.
3. 피싱 공격
피싱 공격은 아시다시피 그럴싸한 링크를 문자(또는 기타 다양한 방식을 통해)로 보내 피해자가 악성 페이지로 연결되는 링크로 접속하도록 하는 공격이다. 일반적으로는 도메인을 숨기려고 short url을 사용하거나, 원래 페이지로 혼동하도록 일반 페이지 링크와 비슷한 형태로 링크를 만든다. (예시로, google.com인척 하려고 go0g1e.com이라는 링크를 사용하는 식으로.) 이마저도 눈치가 조금만 빨라도 금방 피싱 링크인 것을 눈치채고 거들떠도 안보게 된다.
그런데 이렇게 파일 업로드 취약점이 발생하여 공격자가 서버에 임의 파일을 업로드할 수 있다면, 악성 페이지를 하나 만들어 서버에 올리면 이게 완벽한 피싱 링크가 될 수 있다. 멀쩡히 돌아가고 있는 정상적인 서비스라고 생각했는데 까보니 피싱 페이지일 수 있는 것이다. 도메인 이름만 믿고 링크를 누른 사람들은 꼼짝없이 당하는 것이다.
4. 서비스 거부 공격(DoS)
쉽게 말해 서비스를 먹통으로 만드는 것이다. 우리가 많이 들어본 디도스(DDoS, 도스의 일종이다.)는 보통 네트워크 트래픽을 과하게 발생시켜 소위 말하는 ‘서버가 터지는’ 상태를 만드는 것이라면, 파일 업로드 취약점은 약간 다른 방향성으로 간다. 디페이스 공격처럼 기존 페이지 파일을 덮어씌워 페이지를 이용하지 못하게 하는 방법도 있을 것이고, 용량이 매우 매우 큰 파일을 서버에 업로드하여 서버 컴퓨터의 용량을 부족하게 만드는 방식으로도 서비스를 먹통으로 만들 수 있다. 서버의 쉘을 획득한 상태라면 더 다양하게 조질(?) 수 있을 것이다.
파일 업로드 공격 예시
다음은 파일 검증 기능이 존재하지 않아 파일 업로드 취약점이 존재하는 페이지이다. 프로필 사진을 업로드하기 위한 파일 업로드 기능이 존재한다.

이때 다음과 같은 php 파일을 업로드할 수 있다.

파일 검증 기능이 없는 해당 서비스는 위와 같은 웹쉘 코드를 서버 내에 저장하게 된다. 그리고 공격자는 서버 내에서 해당 파일이 저장된 경로를 url에 입력하여 해당 웹쉘을 실행시킬 수 있다.

저장되었다고 한다.

다시 마이페이지로 돌아오면 이미지를 우클릭하여 이미지 주소 복사를 할 수 있다. 이 주소를 주소창에 입력하여 접근하면 된다.

엥?
아무것도 뜨지 않을 것이다. 우리가 작성한 웹쉘의 코드는 url에서 cmd라는 파라미터의 값을 가져와 실행하는 기능이기 때문이다. 우리가 원하는 파일인 /home/carlos/secret의 내용을 보기 위해 cat /home/carlos/secret이라는 명령어를 실행시킨다.

이렇게 업로드한 파일을 통해 서버에서 쉘 명령어를 실행시킨 것을 확인할 수 있다.
(참고로, 현재 출력값은 system 함수의 처리 방식때문에 동일한 결과가 두번 출력되고 있다. 실제 내용은 보이는 내용의 중간까지다.)
File Upload의 방어
확장자 검증
파일 업로드 시 파일의 확장자를 검증한다. 주의할 점은, Content-Type 헤더를 통해 파일의 종류를 검사하면 안된다.

위의 경우, php파일을 직접 올린게 아니라 .txt 파일을 임의로 수정해서 Content-Type이 저모양이지만, 실제 png 파일을 업로드하면 Content-Type은 text/html이다. 하지만 text/plain으로 업로드했을 때도 실제로 실행이 된 것을 보면 추측할 수도 있겠지만, Content-Type 헤더와 실제 파일 내용이 일치하지 않더라도 실행에는 문제가 없기 때문이다.
일단 php 파일을 막는게 목적이니 파일이름 끝에 php만 안오게 하면 안되나 싶기도 하지만 phtml, php3, php5 등 php 확장자가 아니어도 php 코드 실행이 가능한 확장자들이 존재하기 때문이다. 그래서 파일 업로드 기능의 목적에 맞는 파일만 업로드 가능하도록 화이트리스트 방식의 필터링을 적용하는 것이 일단은 가장 안전하다.
그리고 filename.php%00.png 와 같이 NULL 문자를 활용한 확장자 검증 우회 방식도 존재하기 때문에 파일 확장자를 확인하는 로직을 정확히 짜는 것 역시 중요하다.
파일 이름 랜덤화
파일 이름을 랜덤화하는 것이 뭐가 중요할까 싶기도 하지만, 핵심은 파일의 경로를 유추할 수 없도록 하는 것이다. 파일의 경로를 모르면 웹쉘이 업로드되었다 하더라도 공격자가 웹쉘에 접근할 수 없기 때문이다.
하지만 이렇게 하면 서비스 이용자 역시 사용자가 업로드한 파일을 찾지 못한다는 문제가 발생한다. 사용자가 처음 올린 파일 이름과 실제 올라가있는 파일 이름이 다르기 때문이다. 그래서 원래 파일명을 데이터베이스에 저장하는 방식을 사용한다. 그렇게 하면 원래 파일명과 실제 저장된 파일명을 대조할 수 있고 파일 이름이 변경되어도 원래 파일을 찾을 수 있기 때문이다. 그리고 추후 포스팅할 파일 다운로드 취약점을 막을 때에도 해당 방식이 유효하다.