前言
2019年强网杯复现,这一次有很多感触,虽然只是复现,但也学到了很多,超有意思
参考文章:
2019强网杯Web部分题解
张师傅的博客
0x01 upload_img
分析攻击过程
这道题在登录后,我们通过burp抓包会发现,我们的cookie很可疑,然后解码后发现这里的cookie里面的字段有我们上传的图片
1 | user=YTo1OntzOjI6IklEIjtpOjk7czo4OiJ1c2VybmFtZSI7czoxMDoiMTExQHFxLmNvbSI7czo1OiJlbWFpbCI7czoxMDoiMTExQHFxLmNvbSI7czo4OiJwYXNzd29yZCI7czozMjoiNjNhOWYwZWE3YmI5ODA1MDc5NmI2NDllODU0ODE4NDUiO3M6MzoiaW1nIjtzOjc5OiIuLi91cGxvYWQvM2IxNDEyNzUzZjQ3NWNjOTY5YzM3MjMxZGQ2ZWFlYTIvYTVkNWU5OTVmMWE4ODgyY2I0NTllYmEyMTAyODA1Y2QucG5nIjt9 |
解码后:
1 | user=a:5:{s:2:"ID";i:9;s:8:"username";s:10:"111@qq.com";s:5:"email";s:10:"111@qq.com";s:8:"password";s:32:"63a9f0ea7bb98050796b649e85481845";s:3:"img";s:79:"../upload/3b1412753f475cc969c37231dd6eaea2/a5d5e995f1a8882cb459eba2102805cd.png";} |
这道题实际上是一个代码审计题,我们使用dirsearch可以扫到网站源码审计源码
文件../application/web/controller/index.php
代码37行:
1 | public function login_check(){ |
发现这段代码中的我们的请求中的cookie在这儿被解码,并且被反序列化。所以我们可以序列化任意类。
文件../application/web/controller/Profile.php
代码27行:
1 | public function upload_img(){ |
这段代码中的关键代码:
1 | if(!empty($_FILES)){ |
这里的this->filename,this->filename_tmp是可以通过反序列化控制的,此外copy()函数会将filename_tmp的内容复制到filename
那么我们就可先上传一个木马图,然后通过反序列化将filename修改为我们需要的文件名,filename_tmp为我们的木马图文件位置,由此绕过bypass,所以这里我们的目的就是调用Profile类中的upload_img()方法
文件../application/web/controller/Profile.php
代码67行:
1 | public function update_cookie(){ |
这段代码起到更新cookie的作用
然后看向
文件../application/web/controller/Register.php
代码10行:
1 | public function __construct() |
代码58行:
1 | public function __destruct() |
这里$this->checker为Index类,其中$this->registed、$this->checker 在反序列化时也是可控的,然后调用Index类中的index方法
回头看
文件../application/web/controller/Profile.php
代码82行:
1 | public function __get($name) |
这里的get(),以及call()分别为魔术方法
call():当调用一个不存在或者权限不够的方法的时候,会自动调用call()方法
get():当访问一个不存在或者权限不够的属性的时候,会自动调用get()方法
其中$name:被调用的方法名
其中$arguments:被调用的方法的参数列表数组
所以我们可以覆盖$this->checker为Profile类,然后访问index()方法,这时候将会触发call(),然后$this->{$name}=index,这是一个不存在的属性,触发get()方法,从而return $this->except[$name],这里的this->except是可控的属性,通过该属性访问upload_img()方法。从而完成攻击。
POP链如下:
具体操作
上传一个一句话木马图,我的一句话木马图里的内容为’phpinfo()‘
被保存在../upload/3b1412753f475cc969c37231dd6eaea2/a5d5e995f1a8882cb459eba2102805cd.png
编写EXP生成cookie:
1 |
|
上传这段cookie,然后刷新一下,跳出错误,继续上传这段cookie,然后再刷新,然后访问info.php
小结
(1)反序列化的知识还需加强
(2)在审计代码时,要注意哪些属性是可控的,在编写exp时候,需要注意哪些属性我们需要加入,比如public $registed = false;以及public $ext = true;
随便注
万能密码先试一下:
1 | 1'or '1'= '1 |
尝试注入,
1 | 1' union select database()# |
回显:return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
堆叠注入为注入方式:
1 | 1';show tables;# |
回显:
1 | array(2) { |
考虑到过滤掉很多关键字,所以这里考虑使用sql预编译功能
MySQL官方将prepare、execute、deallocate统称为PREPARE STATEMENT
示例代码:
1 | PREPARE stmt_name FROM preparable_stmt |
举个例子:
1 | mysql> PREPARE pr1 FROM 'SELECT ?+?'; |
每一次执行完EXECUTE时,养成好习惯,须执行DEALLOCATE PREPARE … 语句,这样可以释放执行中使用的所有数据库资源(如游标)
所以这里我们的payload可以这样考虑
1 | 1'; |
回显:strstr($inject, "set") && strstr($inject, "prepare")
使用大小写绕过,成功
然后获取flag的payload为:
1 | 1'; |
这里有一个需要注意的小问题就是,在“select * from 数据表名”,数据表名要用反引号。