RWTH CTF 2012 - Azure Coast - writeup part1
0. traffic dumps
look at the following traffic dumps (images are clickable):they show a conversation between service (blue) and checking system (red)
You can download original PCAPs if you want: stream1.pcap , stream2.pcap
You can see that every conversation begins with four bytes of data server sends to client and then client answers with different four bytes.
1. challenge & response
Start with writing small script that reads data from server and writes something back:
socket = TCPSocket.new @host, TARGET_PORT
chal = socket.read(4).unpack('L')[0]
printf " in: %08x %10d\n", chal&0xffffffff, chal
resp = 0x11223344
outdata = [resp].pack('L')
data = outdata.unpack('L')[0]
printf "out: %08x %10d\n", data&0xffffffff, data
socket.write outdata
# in: e50a481c 3842656284
# out: 11223344 287454020
diff -ur azurecoast_orig/service_source/service/go/src/rwthctfvm/cpu/cpu.go azurecoast/service_source/service/go/src/rwthctfvm/cpu/cpu.go
--- azurecoast_orig/service_source/service/go/src/rwthctfvm/cpu/cpu.go 2012-12-14 00:24:07.277183829 +0400
+++ azurecoast/service_source/service/go/src/rwthctfvm/cpu/cpu.go 2012-12-01 00:47:51.611877564 +0400
@@ -159,12 +159,13 @@
if c.Step { getKey() }
ipval,err := c.GetRegister(ip)
//fmt.Println("fetching "+hex(ipval))
+ fmt.Print(hex(ipval) + ": ")
if err != nil {return errors.New("unable to get ip") }
instrval, err := c.GetMemory(ipval)
if err != nil {return errors.New("unable to get cmd") }
instr, err := c.GetInstr(instrval)
if err != nil {return errors.New("unable to decode instr") }
- //fmt.Println(instr.Inspect())
+ fmt.Println(instr.Inspect())
if c.Step {
fmt.Print("IP: "+c.InspectIP())
fmt.Println(instr.Inspect())
@@ -287,9 +288,9 @@
i := int(bytes[0])
if i!=0x0a { //don't print newlines
- //c.Log("readb from",arg1,"read", hex(int(i)), string(i), "ok:", num)
+ c.Log("readb from",arg1,"read", hex(int(i)), string(i), "ok:", num)
} else {
- //c.Log("readb from",arg1,"read", hex(int(i)), "ok:", num)
+ c.Log("readb from",arg1,"read", hex(int(i)), "ok:", num)
}
c.SetRegister(t1,num)
c.SetRegister(t0,i)
that will provide us with nice detailed execution logs like
0x0: <ldw t0(0x0) 0x0 >
0x2: <ldw t6(0x0) 0x0 >
0x4: <jmp t6(0x2ce) 0x0 >
0x2ce: <mov t4(0x0) t0(0x5) >
0x2cf: <sys 0x9 0x0 >
0x2d0: <mov t2(0x0) t0(0x1f9c) >
0x2d1: <sub t2(0x1f9c) t1(0x9e2) >
Now remember that we got a 0xe50a481c
challenge value.
and responded with 0x11223344
So, let's grep it out of the execution log!
0x351: <jmp t6(0x3d1) 0x0 >
0x3d1: <mov t0(0x2410f8a0) t1(0xffffffffe50a481c) >
0x3d2: <sys 0x4 0x1 >
0x3d3: <ldw t1(0xffffffffe50a481c) 0x0 >
0x3d5: <xor t0(0xffffffffe50a481c) t1(0xffffffffa7f7e284) >
[.] R=0x42fdaa98
0x3d6: <mul t0(0x42fdaa98) t0(0x42fdaa98) >
[.] R=0x21e3a40
0x3d7: <ldw t1(0xffffffffa7f7e284) 0x0 >
0x3d9: <add t0(0x21e3a40) t1(0xffffffffa82d3274) >
[.] R=0xffffffffaa4b6cb4
0x3da: <mov t2(0x2410f8a0) t0(0xffffffffaa4b6cb4) >
0x3db: <sys 0x3 0x0 >
0x3dc: <cmp t0(0x11223344) t2(0xffffffffaa4b6cb4) >
Yay! Better than I expected! XOR, then MUL, then ADD.
2. We're in!
So, final challenge-response code is:
socket = TCPSocket.new @host, TARGET_PORT
chal = socket.read(4).unpack('L')[0]
printf " in: %08x %10d\n", chal&0xffffffff, chal
resp = (chal^0xa7f7e284)**2 + 0xa82d3274
outdata = [resp].pack('L')
data = outdata.unpack('L')[0]
printf "out: %08x %10d\n", data&0xffffffff, data
socket.write outdata