웹셸과 서버 침해¶
언제 쓰나
인터넷에 노출된(또는 내부) 웹 서버가 이상하게 동작할 때: 웹 루트의 낯선 스크립트, 셸을 띄우는 웹 서버 프로세스(w3wp.exe, httpd, tomcat, php-fpm), 또는 앱에 대한 익스플로잇 경보. 웹셸은 흔한 최초 침투이자 지속성 발판입니다 — HTTP 요청을 서버의 명령 실행으로 바꿔 주는 작은 스크립트입니다.
1. 트리아지 (첫 15분)¶
- 확인: 디스크에 웹셸(개발자가 넣지 않은 웹 루트의 스크립트)이 있나, 그리고/또는 웹 서버 프로세스가 자식 프로세스(cmd/powershell/sh)를 띄우나?
- 서버 역할과 노출 정도를 파악하세요 (인터넷에 노출? 어떤 앱/CMS/버전?). 알려진 취약 앱 + 새 스크립트 = 익스플로잇당함.
- 무언가 정리되기 전에 웹셸 파일과 관련 웹 서버 접근 로그를 보존하세요 — 접근 로그가 요청을 셸과 공격자 IP에 연결합니다.
- 지금 쓰이고 있다면 서버를 네트워크에서 격리하세요 (RAM/아티팩트를 위해 실행은 유지).
2. 수집¶
| 출처 | 무엇 | 어디서 |
|---|---|---|
| 웹 서버 접근/오류 로그 | 셸로의 요청, 공격자 IP, 익스플로잇 시도 | IIS (%SystemDrive%\inetpub\logs\LogFiles), Apache/Nginx (/var/log/…), Tomcat |
| 웹셸 파일 | 페이로드 자체 | 웹 루트; 해시하고 읽어 보기 |
| 호스트 트리아지 | 서버에서의 실행, 지속성, 횡적 이동 | KAPE / Velociraptor (Linux 아티팩트도) |
| $MFT / $UsnJrnl | 셸이 언제 (무엇에 의해) 쓰였나 | 파일 생성 시각 = 업로드 시각 |
| 네트워크 | 익스플로잇 요청 + 후속 C2/횡적 이동 | Zeek http.log / Arkime |
| 앱/DB 로그 | SQLi, 인증 우회, 관리 작업 | 애플리케이션별 |
3. 분석¶
- 셸 찾기: 셸을 띄우는 웹 서버 프로세스가 가장 시끄러운 신호입니다 (수상한 부모 —
w3wp.exe/httpd/tomcat→cmd/powershell/sh/whoami). 디스크에서는 수상한 내용(eval,system(,exec(,passthru(,Runtime.exec, base64 덩어리)이 든, 웹 루트에 최근 만들어진 스크립트. 해시해서 전사를 검색하세요 — 공격자는 같은 셸을 여러 서버에 떨어뜨립니다. - 언제, 어떻게 들어왔나: 셸의
$MFT/$UsnJrnl생성 시각 = 업로드 순간; 접근 로그 항목(그 경로로의PUT/POST, 또는 직전의 익스플로잇 요청)과 상관분석하세요. 그러면 최초 침투 경로(업로드 기능, 역직렬화, RCE, 경로 탐색, 탈취된 자격증명)가 나옵니다. - 공격자가 셸로 무엇을 했나: 접근 로그가 셸로의 모든 요청을 보여 줍니다 —
POST본문(기록된다면)이나 이어진 자식 프로세스(Sysmon 1)가 명령을 드러냅니다:whoami,net user, 도구 다운로드, 자격증명 덤프, 횡적 이동. - 공격자 IP와 범위: 셸에 접속한 출발지 IP → 그들이 건드린 다른 경로, 다른 서버, 침해 전 정찰(
404스캔)을 검색하세요. 셸 하나는 대개 더 있다는 뜻입니다. - 셸 외의 지속성: 서비스/작업/Run 키, 새 웹 앱 관리자 계정, 수정된 앱 설정, 추가 셸(공격자는 예비용을 심어 둠)을 확인하세요.
4. 봉쇄 / 제거¶
- 웹셸을 모두 제거하고(전부 찾으세요 — 예비용을 심어 둡니다) 공격자가 추가한 계정, 작업, 서비스도 제거하세요.
- 업로드/RCE를 허용한 취약한 앱/기능을 패치하거나 끄세요 — 경로를 막지 않고 셸만 지우면 다시 침해당할 뿐입니다.
- 서버가 갖고 있던 자격증명/비밀 정보(앱 서비스 계정, DB 자격증명, API 키, 머신 키)를 교체하세요 — 읽혔다고 가정하세요.
- 공격자가 서버에서 다른 곳으로 이동했다면 횡적 이동 / 도메인으로 넘어가세요.
- 공격자 IP를 차단하고; 익스플로잇에 대한 WAF 규칙을 고려하세요.
5. 복구와 교훈¶
- 침해가 파일 하나 제거로 끝나는 수준을 넘었다면 깨끗한 매체로 서버를 재구축하세요 — 웹 서버의 모든 셸/백도어를 찾았다고 확신할 수 없습니다.
- 앱은 패치하고, 비밀 정보를 교체하고, WAF 뒤에 두고, 웹 루트 쓰기 권한을 줄여서 복원하세요 (웹 프로세스가 자기 루트에 실행 가능한 스크립트를 쓸 수 없어야 합니다).
- 강화: 최소 권한 앱 풀, 업로드 폴더에서 스크립트 실행 금지, 웹 루트 파일 무결성 모니터링, 셸을 띄우는 웹 서버 프로세스에 대한 Sysmon 규칙, 셸 같은 요청 패턴에 대한 지속적인 접근 로그 모니터링.
유용한 쿼리¶
index=botsv3 sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventID=1
| eval parent=lower(replace(ParentImage,".*\\\\",""))
| where parent IN ("w3wp.exe","httpd.exe","nginx.exe","php-cgi.exe","php-fpm","tomcat*.exe","java.exe","sqlservr.exe")
AND match(lower(Image),"cmd\.exe|powershell|whoami|net1?\.exe|cscript|wscript|bitsadmin|certutil|/bin/(ba)?sh")
| table _time, host, parent, Image, CommandLine, User
| sort 0 _time
Sysmon 1 = 프로세스 생성. 셸이나 탐색 명령을 띄우는 웹 서버/앱 프로세스는 웹셸을 정의하는 신호입니다 — 정상 웹 앱은 거의 이러지 않습니다. 비교를 위해 parent는 파일명만 남겼습니다.
index=<your_web_index> sourcetype=iis
| stats count, values(c_ip) as src, values(cs_method) as methods, min(_time) as first, max(_time) as last by cs_uri_stem
| where match(cs_uri_stem,"(?i)\.(aspx?|php|jsp|jspx)$") AND match(mvjoin(methods,","),"POST")
| where count < 50
| convert ctime(first) ctime(last)
| sort first
요청한 페이지별로 묶은 웹 서버 접근 로그. 소수의 출발지에서 POST를 받고 거의 접속되지 않는(count가 낮은) 스크립트 엔드포인트 — 특히 파일명이 앱에 속하지 않는 것 — 는 웹셸 후보입니다; first/last가 활동 기간을, src가 공격자 IP를 알려 줍니다. 필드 이름(cs_uri_stem, c_ip, cs_method)은 데이터 탐색으로 자기 로그 소스에 맞게 바꾸세요.