:串口通信pwnlib

目录

【pwn基础】Pwntools学习Pwntools介绍

Pwntools 是一个非常知名的 CTF 框架和漏洞利用开发库,允许用户快速编写漏洞利用。

具有本地执行、远程连接读写、shellcode生成、ROP链构建、ELF解析、符号泄漏等诸多强大功能。

Pwntools 安装

因为是python库,所以可以直接使用pip来管理安装。

#提前安装pip
sudo apt-get install python3-pip
#安装pwntools
pip install pwntools -i https://pypi.tuna.tsinghua.edu.cn/simple

测试是否安装成功

Pwntools常用模块和功能

pwntools分为两个模块,一个是pwn,简单的使用from pwn import *将所有子模块和一些常用系统库导入到当前命名空间,专门针对CTF比赛优化。

另外一个模块是pwnlib,适合开发成产品,根据自己的需要导入不同的子模块。

pwnlib.tubes 模块学习

tubes模块是一个主要用于通信的模块。应该是pwn题中使用最广泛的交互方式。主要有以下4种通讯方式。

pwnlib.tubes.process:进程通讯 pwnlib.tubes.serialtube:串口通讯 pwnlib.tubes.sock:socket socket通讯 pwnlib.tubes.ssh:SSH连接通讯tube.process

这里一直想知道这个进程通信是怎么做的,为什么我用代码p=process(‘./mydemo’),然后可以用send和recv发送程序,然后看他的源码注释 生成一个新进程,用管子包起来通信,他应该是用比较hacky的方法来模拟系统加载本地程序到进程中的操作,在上面封装了一层管子通信,所以我们可以通过 send 和 recv 与他创建的流程进行通信,因此它为我们创造了无数的可能性。比如我之前的文章【二进制漏洞】Stack Overflow Vulnerability Linux一章的主题scanf只能输入ASCII码,所以我们无法构造地址Payload,但是有了tube.process就可以轻松搞定。

#include 
void hack()
{
    printf("Hack Success!!!!n");
}
int main()
{
    printf("Hello,Please Start Hack!n");
    char buf[20]={0};
    scanf("%s",buf);
    printf("Your input:%sn",buf);
    int i;
    for(i=0;i<sizeof(buf);i++)
    {
        printf("0x%x,",buf[i]);
    }
    return 0;
}

gcc hack2.c -m32 -fno-stack-protector -z noexecstack -o hack2

正常情况下:0x01、0x02、0x03不能输入。

通过tubes.process,send可以用来发送原始的十六进制数据。

#导入pwntools模块
from pwn import *
context(arch = 'i386',os='linux')
p = process("./hack2")
#显示程序运行的第一条回显

图片[1]-:串口通信pwnlib-唐朝资源网

print(p.recv()) #利用pipe管道发送带 十六进制的数据 p.sendline(b'AAAA'+b'x01x02x03x04') #回显结果 print(p.recvline()) print(p.recvline())

所以[Binary Vulnerability] Stack Overflow Vulnerability Linux文章标题用tube.process方便多了。

打印进程加载起始地址。

p = process("./hack")
imageBase = p.libs()["/home/ubuntu/hack"]

远程使用以下命令:

conn = remote('exploitme.example',31337)
conn.recv()
conn.sendline('test')

pwnlib.context(运行环境)

该模块主要用于设置进程的运行环境,比如目标是什么CPU架构,多少位数,什么平台,是否开启日志等。

#架构32位X86,平台Linux
context(arch='i386',os='linux')
#设置tmux分屏
context.terminal['tmux','splitw','-h']
#开启日志信息
context.log_level = 'debug'

CPU架构如下:

architectures = _longest({
        'aarch64':   little_64,
        'alpha':     little_64,
        'avr':       little_8,
        'amd64':     little_64,
        'arm':       little_32,
        'cris':      little_32,
        'i386':      little_32,
        'ia64':      big_64,
        'm68k':      big_32,
        'mips':      little_32,
        'mips64':    little_64,
        'msp430':    little_16,
        'powerpc':   big_32,
        'powerpc64': big_64,
        'riscv':     little_32,
        's390':      big_32,
        'sparc':     big_32,
        'sparc64':   big_64,
        'thumb':     little_32,

图片[2]-:串口通信pwnlib-唐朝资源网

'vax': little_32, 'none': {}, }) transform = [('ppc64', 'powerpc64'), ('ppc', 'powerpc'), ('x86-64', 'amd64'), ('x86_64', 'amd64'), ('x86', 'i386'), ('i686', 'i386'), ('armv7l', 'arm'), ('armeabi', 'arm'), ('arm64', 'aarch64')]

数字:

    big_32    = {'endian': 'big', 'bits': 32}
    big_64    = {'endian': 'big', 'bits': 64}
    little_8  = {'endian': 'little', 'bits': 8}
    little_16 = {'endian': 'little', 'bits': 16}
    little_32 = {'endian': 'little', 'bits': 32}
    little_64 = {'endian': 'little', 'bits': 64}

平台:

 oses = sorted(('linux','freebsd','windows','cgc','android','baremetal'))

pwnlib.elf(ELF文件操作)

pwnlib.elf 模块非常实用。虽然linux下有头文件可以用来解析ELF文件,但是很多代码必须自己实现。该模块解决了这些实现,可以进行符号搜索、虚拟内存、文件偏移、修改和保存二进制等。

from pwnlib.elf import ELF
#构造类
elf = ELF('./hack_dyn')
#架构,位数,平台
print("---------------------------------------------")
print("[+]架构:{0} 位数:{1} 系统:{2}".format(elf.arch,elf.bits,elf.os))
print("")
#打印装载地址
print("[*]装载地址:",hex(elf.address))
#打印GOT表
print("[*]GOT表:")
for kv in elf.got.items():
    print(kv)
#打印PLT表
print("")
print("[*]PLT表:")
for kv in elf.plt.items():
    print(kv)
print("[*]hack函数偏移:",hex(elf.symbols['hack']))
print("---------------------------------------------")

可以看到HOT表、PLT表、符号表中hack函数的偏移量打印出来了。加载地址为0,因为这是一个动态链接程序,加载地址是不确定的。改为静态编译显示加载地址。

图片[3]-:串口通信pwnlib-唐朝资源网

静态链接

asm(address,assembly) # 汇编指令assembly插入ELF的address地址处,需要使用save函数来保存
bss(offset) # 返回.bss段加上offset后的地址
checksec() # 查看文件开启的安全保护
disable_nx() # 关闭NX
disasm(address,n_bytes) # 返回地址address反汇编n字节的字符串
offset_to_vaddr(offset) # 将偏移offset转换为虚拟地址
vaddr_to_offset(address) # 从虚拟地址address转换为文件偏移
read(address,count) # 从虚拟地址address读取count个字节的数据
write(address,data) # 在虚拟地址address写入data
section(name) # 获取name段的数据
debug() # 使用gdb.debug()进行调试

pwnlib.asm(汇编模块)

这是一个非常强大的模块,可以组装和反汇编,通常在开发shellcode时非常有用。

可以先使用pwnlib.context设置CPU架构、字节序、位数。

用asm()函数汇编,disasm()函数反汇编

from pwnlib.asm import *
#汇编
print(asm('mov eax, 0'))
print(asm('mov ebx, 1'))
print(asm('add eax, ebx'))
print(asm('mov eax, SYS_execve'))
print(asm('nop'))

disasm() 反汇编

from pwnlib.asm  import *
from pwnlib.util.fiddling import *
#反汇编
print(disasm(unhex('E007BFA9E20FBFA9E417BFA9E61FBFA9E827BFA9FA6FBFA9FC77BFA9FE0F1FF8C81580D2010000D440050035881580D2010000D41F040071C1040054000080D261FCFF10021880D2E3031FAA080780D2010000D4E003F837FF4300D1481680D2010000D4E00B00B9881B80D2E4230091E3031FAAE2031FAAE1031FAA200280D20024A0F2010000D4E00B00B9FF43009100020035000080D241FAFF300201A0D2E3031FAA080780D2010000D4E40300AAC81B80D2000080D2230080D2E5031FAAA20080D2010094D20100A0F2010000D4FE0741F8FC77C1A8FA6FC1A8E827C1A8E61FC1A8E417C1A8E20FC1A8E007C1A8FD7BBEA9010000142C010000000000000000000000000000000000000000000000000000AA0400000000F1FF'),arch='aarch64',bits=64))

装拆时,其他架构平台,记得安装对应的Binutils,安装教程:

反编译效果真的很强大!

pwnlib.shellcraft(Shellcode 生成器)

这个模块可以用来生成Shellcode代码,这种模块好可爱,可以生成aarch64、arm,thumb,mips,i386、amd64、powerpc架构shellcode代码,基本结构都有了。

生成 Shellcode 代码。

>>> from pwn import *
>>> print(shellcraft.i386.nop())
    nop
   #生成了一个x86架构平台的nop

接下来生成安卓手机的Shellcode打开/data/local/tmp/test.txt,这个模块太强大了。

#设置CPU架构  aarch64
#设置系统平台 android
print(shellcraft.aarch64.android.open('/data/local/tmp/test.txt'))

图片[4]-:串口通信pwnlib-唐朝资源网

还可以用asm输出不同格式的shellcode,真是方便好用!

from pwn import *
shellcode = shellcraft.aarch64.android.open('/data/local/tmp/test.txt')
print("输出字符串格式Shellcode:")
print(asm(shellcode,arch='aarch64',bits=64,os='android'))
print("")
print("输出十六进制格式Shellcode:")
print(asm(shellcode,arch='aarch64',bits=64,os='android').hex())

官方示例。

from pwnlib.shellcraft import *
context.clear()
context.arch = 'amd64'
sc = 'push rbp; mov rbp, rsp;'
sc += shellcraft.echo('Hellon')
sc += 'mov rsp, rbp; pop rbp; ret'
solib = make_elf_from_assembly(sc, shared=1)
subprocess.check_output(['echo', 'World'], env={'LD_PRELOAD': solib}, universal_newlines = True)
'HellonWorldn'

pwnlib.util(小工具)

这个模块是一些常用的功能。比如之前使用的 unhex 就是来自这个模块。另外还有packing、hash、net、misc、sh_string、cyclic等函数。

#用的最多的应该是pack函数了吧
p8(0) #打包1字节
b'x00'
p32(0xdeadbeef) #32位最常用的,打包4字节
b'xefxbexadxde'
p64(0xdeadbeef)
b'xefxbexadxdex00x00x00x00'
#可设置大小端序
>>> p32(0xdeadbeef,endian='little')
b'xefxbexadxde'
>>> p32(0xdeadbeef,endian='big')
b'xdexadxbexef'
#解包
unpack(b'xaax55',16,endian='little')
'0x55aa'
u32('xaax55x00x00')
21930
u64('xaax55x00x00x00x00x00x00')
21930
#生成溢出字符串(cyclic)
cyclic(20)
b'aaaabaaacaaadaaaeaaa'

cyclic(20, alphabet=string.ascii_uppercase) #全大写
b'AAAABAAACAAADAAAEAAA'  
cyclic(20, n=8)		    #8字符对齐
b'aaaaaaaabaaaaaaacaaa' 
cyclic(20, n=2)         #2字符对齐
b'aabacadaeafagahaiaja' 
cyclic(alphabet = "ABC", n = 3)#设置成ABC对齐
b'AAABAACABBABCACBACCBBBCBCCC'
context.cyclic_alphabet = "ABC" #全局修改
cyclic(10)
b'AAAABAAACA'
#查找偏移
cyclic_find('daaa')
12
cyclic_find(0x61616162)
4
#unhex
unhex('0102030405060708')
b'x01x02x03x04x05x06x07x08'

pwnlib.rop

rop利用模块,包括rop、srop等

当前的漏洞利用变得越来越困难。一般来说,开始问题必须由 NX 启用。以前可以给400分的ROP,现在50-100分就很惨了。这个工具是否简化了 ROP 过程? “错了”

让我们简单回顾一下ROP的原理。由于NX不能在栈上执行shellcode,我们可以在栈上安排一系列的返回地址和参数,这样就可以进行多个函数调用,通过函数末尾的ret语句来控制程序的流程,并在程序中使用一些 pop/ret 代码块(称为小工具)来平衡堆栈。它所做的只是把/bin/sh,覆盖程序中一个函数的GOT作为system,然后ret到那个函数的plt可以触发system(‘/bin/sh’)。因为它是使用 ret 指令的漏洞利用,所以它被称为 Return-Oriented Programming。 (如果没有开启ASLR,可以直接使用ret2libc技术)

嗯,从这个角度来说,这个技术的难点自然是如何在栈上安排返回地址和函数参数。 ROP模块的作用是自动找到程序中的gadget,并自动在栈上部署相应的参数。

from pwn import *
elf = ELF('ropasaurusrex')
rop = ROP(elf)
rop.read(0, elf.bss(0x80))
rop.dump()
# ['0x0000:        0x80482fc (read)',
#  '0x0004:       0xdeadbeef',
#  '0x0008:              0x0',
#  '0x000c:        0x80496a8']
str(rop)
# 'xfcx82x04x08xefxbexadxdex00x00x00x00xa8x96x04x08'

使用 ROP(elf) 生成一个 rop 对象。这时候rop链还是空的,需要加一个函数。

因为ROP对象实现了__getattr__的功能,所以可以直接以func调用的形式添加功能。 rop.read(0, elf.bss(0x80)) 实际上等价于rop.call(‘read’, (0, elf.bss(0x80)))。通过添加函数调用多个次,最后使用 str 转储整个 rop 链。

参考文章:

(pwntools 学习)

(漏洞利用工具–Pwntools)

基础工具-pwntools/(pwn基础工具-pwntools)

(Getshell Remote:真正的RCE连接?反向连接?没有连接?)

(pwntools源代码)

最后,您可以查看 pwntools 源代码以熟悉它。各个模块的功能一般都用demo写的还是很详细的。

PWN鸡队

最后,感谢您的阅读。我正在学习这道菜。文章如有错误,请及时指出。

你也可以来群里骂我哈哈哈,群里有PWN、RE、WEB大佬,欢迎交流

© 版权声明
THE END
喜欢就支持一下吧
点赞221 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片