Skip to content

Commit

Permalink
attempt to fix #19
Browse files Browse the repository at this point in the history
  • Loading branch information
Etienne Stalmans committed Apr 6, 2017
1 parent c96e928 commit 31a6680
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 13 deletions.
11 changes: 11 additions & 0 deletions mapi/datastructs-abk.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ type BindRequest struct {
AuxiliaryBuffer []byte
}

type BindRequestRPC struct {
Flags uint32
State []byte //optional 36 bytes
ServerGUID []byte
}

//BindResponse struct
type BindResponse struct {
StatusCode uint32
Expand Down Expand Up @@ -178,6 +184,11 @@ func (bindRequest BindRequest) Marshal() []byte {
return utils.BodyToBytes(bindRequest)
}

//Marshal turn BindRequestRPC into Bytes
func (bindRequest BindRequestRPC) Marshal() []byte {
return utils.BodyToBytes(bindRequest)
}

//Marshal turn GetSpecialTableRequest into Bytes
func (specialTableRequest GetSpecialTableRequest) Marshal() []byte {
return utils.BodyToBytes(specialTableRequest)
Expand Down
34 changes: 32 additions & 2 deletions mapi/mapi-abk.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package mapi
import (
"fmt"

rpchttp "github.com/sensepost/ruler/rpc-http"
"github.com/sensepost/ruler/utils"
)

func sendAddressBookRequest(mapiType string, mapi []byte) ([]byte, error) {
if AuthSession.Transport == HTTP {
return mapiRequestHTTP(AuthSession.ABKURL.String(), mapiType, mapi)
}
return nil, nil //mapiRequestRPC(mapi)

//return rpchttp.EcDoRPCExt2(mapi, 0)
return rpchttp.EcDoRPCAbk(mapi, 0)
//return nil, nil
}

//ExtractMapiAddressBookURL extract the External mapi url from the autodiscover response
Expand All @@ -29,14 +33,40 @@ func BindAddressBook() (*BindResponse, error) {
bindReq := BindRequest{}
bindReq.Flags = 0x00
bindReq.HasState = 0xFF
bindReq.State = STAT{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1252, 1033, 2057}.Marshal()
bindReq.State = STAT{0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFFFFFF, 1252, 1033, 2057}.Marshal()
bindReq.AuxiliaryBufferSize = 0x00

responseBody, err := sendAddressBookRequest("BIND", bindReq.Marshal())

if err != nil {
return nil, fmt.Errorf("A HTTP server side error occurred.\n %s", err)
}

bindResp := BindResponse{}
_, err = bindResp.Unmarshal(responseBody)
if err != nil {
return nil, err
}
return &bindResp, nil

//return nil, fmt.Errorf("unexpected error occurred")
}

//BindAddressBookRPC function to bind to the AddressBook provider
func BindAddressBookRPC() (*BindResponse, error) {

bindReq := BindRequestRPC{}
bindReq.Flags = 0x00
bindReq.State = STAT{0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFFFFFF, 1252, 1033, 2057}.Marshal()
bindReq.ServerGUID = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x30, 0x1f, 0x00, 0x17, 0x3a, 0x1f, 0x00, 0x08, 0x3a, 0x1f, 0x00, 0x19, 0x3a, 0x1f, 0x00, 0x18, 0x3a, 0x1f, 0x00, 0xfe, 0x39, 0x1f, 0x00, 0x16, 0x3a, 0x1f, 0x00, 0x00, 0x3a, 0x1f, 0x00, 0x02, 0x30, 0x02, 0x01, 0xff, 0x0f, 0x03, 0x00, 0xfe, 0x0f, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x05, 0x39}

data := bindReq.Marshal()
responseBody, err := rpchttp.EcDoRPCAbk(data, len(bindReq.ServerGUID)-10)

if err != nil {
return nil, fmt.Errorf("A HTTP server side error occurred.\n %s", err)
}

bindResp := BindResponse{}
_, err = bindResp.Unmarshal(responseBody)
if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions mapi/mapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,11 @@ func mapiConnectRPC(body ConnectRequestRPC) ([]byte, error) {
ready := make(chan bool) //this is our ready channel,
chanError := make(chan error) //get the error message from our channel setup

utils.Trace.Println("Setting up channels")
//we should add a channel to check if there was an error setting up the channels
//there will currently be a deadlock here if something goes wrong
go rpchttp.RPCOpen(AuthSession.RPCURL, ready, chanError)

utils.Trace.Println("Setting up channels")

//wait for channels to be setup
if v := <-ready; v == false { //check if the setup was successful or premission Denied
e := <-chanError
Expand Down Expand Up @@ -982,7 +981,7 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) {
deleteFolder := RopDeleteFolderRequest{RopID: 0x1D, LogonID: AuthSession.LogonID}
deleteFolder.InputHandle = 0x00
deleteFolder.FolderID = folderid
deleteFolder.DeleteFolderFlags = 0x05
deleteFolder.DeleteFolderFlags = 0x10 | 0x04 | 0x01

fullReq := deleteFolder.Marshal()

Expand Down
53 changes: 48 additions & 5 deletions rpc-http/rpctransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var rpcOutR, rpcOutW = io.Pipe()
var rpcRespBody *bufio.Reader
var callcounter int
var responses = make([]RPCResponse, 0)
var httpResponses = make([][]byte, 0)
var rpcntlmsession ntlm.ClientSession

//AuthSession Keep track of session data
Expand Down Expand Up @@ -152,7 +153,6 @@ func setupHTTP(rpctype string, URL string, ntlmAuth bool, full bool) (net.Conn,
}
}


if cookiestr != "" {
request = fmt.Sprintf("%sCookie: %s\r\n", request, cookiestr)
}
Expand All @@ -179,6 +179,15 @@ func RPCOpen(URL string, readySignal chan bool, errOccurred chan error) (err err
//this will be sent back to the caller through "readySignal", while error is sent through errOccurred
go RPCOpenOut(URL, readySignal, errOccurred)

select {
case <-readySignal:
readySignal <- false
errOccurred <- err
return err
case <-time.After(time.Second * 2): // call timed out
readySignal <- true
}

for {
data := make([]byte, 2048)
n, err := rpcInR.Read(data)
Expand All @@ -204,13 +213,15 @@ func RPCOpenOut(URL string, readySignal chan bool, errOccurred chan error) (err
errOccurred <- err
return err
}
readySignal <- true

scanner := bufio.NewScanner(rpcOutConn)
scanner.Split(SplitData)

for scanner.Scan() {
if b := scanner.Bytes(); b != nil {
if string(b[0:4]) == "HTTP" {
httpResponses = append(httpResponses, b)
}
r := RPCResponse{}
r.Unmarshal(b)
r.Body = b
Expand Down Expand Up @@ -248,8 +259,12 @@ func RPCBind() error {

RPCWrite(bind.Marshal())

RPCRead(0)
RPCRead(0)
if _, err := RPCRead(0); err != nil {
return err
}
if _, err := RPCRead(0); err != nil {
return err
}

//parse out and setup security
if AuthSession.RPCNetworkAuthLevel == RPC_C_AUTHN_LEVEL_PKT_PRIVACY {
Expand Down Expand Up @@ -319,9 +334,23 @@ func EcDoRPCExt2(MAPI []byte, auxLen uint32) ([]byte, error) {
sec.Unmarshal(resp.SecTrailer, int(resp.Header.AuthLen))
return dec[20:], err
}

return resp.PDU[28:], err
}

func EcDoRPCAbk(MAPI []byte, l int) ([]byte, error) {
RPCWriteN(MAPI, uint32(l), 0x03)
//RPCWrite(req.Marshal())

resp, err := RPCRead(callcounter - 1)

if err != nil {
return nil, err
}
fmt.Printf("%x\n", resp.PDU)
return resp.PDU, err
}

//DoConnectExRequest makes our connection request. After this we can use
//EcDoRPCExt2 to make our MAPI requests
func DoConnectExRequest(MAPI []byte, auxLen uint32) ([]byte, error) {
Expand Down Expand Up @@ -405,6 +434,7 @@ func RPCWriteN(MAPI []byte, auxlen uint32, opnum byte) {
pdu.ContextHandle = AuthSession.ContextHandle
}
pdu.Data = MAPI

pdu.CbAuxIn = uint32(auxlen)
pdu.AuxOut = 0x000001008

Expand Down Expand Up @@ -466,6 +496,7 @@ func RPCOutWrite(data []byte) {
//our list of received responses. Blocks until it finds a response
func RPCRead(callID int) (RPCResponse, error) {
c := make(chan RPCResponse, 1)
cerr := make(chan error, 1)
go func() {
stop := false
for stop != true {
Expand All @@ -480,9 +511,21 @@ func RPCRead(callID int) (RPCResponse, error) {
}
}()

go func() {
for _, v := range httpResponses {
st := string(v)
if er := strings.Split(strings.Split(st, "\r\n")[0], " "); er[1] != "200" {
cerr <- fmt.Errorf("Invalid HTTP response: %s", er)
break
}
}
}()

select {
case resp := <-c:
return resp, nil
case er := <-cerr:
return RPCResponse{}, er
case <-time.After(time.Second * 10): // call timed out
return RPCResponse{}, fmt.Errorf("Time-out reading from RPC")
}
Expand All @@ -500,7 +543,7 @@ func SplitData(data []byte, atEOF bool) (advance int, token []byte, err error) {
for k := range data {
if data[k] == 0x0d && data[k+1] == 0x0a && data[k+2] == 0x0d && data[k+3] == 0x0a {
//utils.Trace.Print(string(data[:k+4]))
return k + 4, nil, nil //data[0:k], nil
return k + 4, data[0:k], nil //data[0:k], nil
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions ruler.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,11 +384,13 @@ func printRules() error {

//Function to display all addressbook entries
func abkList(c *cli.Context) error {
utils.Trace.Println("Let's play addressbook")
if config.Transport == mapi.RPC {
return fmt.Errorf("Address book support is currently limited to MAPI/HTTP")
return fmt.Errorf("Only MAPI/HTTP is currently supported for addressbook interaction")
}
utils.Trace.Println("Let's play addressbook")

mapi.BindAddressBook()

columns := make([]mapi.PropertyTag, 2)
columns[0] = mapi.PidTagDisplayName
columns[1] = mapi.PidTagSMTPAddress
Expand Down Expand Up @@ -463,7 +465,7 @@ func abkDump(c *cli.Context) error {
if len(rows.RowData[k].AddressBookPropertyValue) == 2 {
disp := utils.FromUnicode(rows.RowData[k].AddressBookPropertyValue[0].Value)
email := utils.FromUnicode(rows.RowData[k].AddressBookPropertyValue[1].Value)
if _, err := fout.WriteString(fmt.Sprintf("%s , %s\n", disp, email)); err != nil {
if _, err := fout.WriteString(fmt.Sprintf("%s | %s\n", disp, email)); err != nil {
return fmt.Errorf("Couldn't write to file... %s", err)
}
}
Expand Down

0 comments on commit 31a6680

Please sign in to comment.