mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
modify
This commit is contained in:
parent
ec87e74daf
commit
25ddafd9e7
@ -49,8 +49,6 @@ long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
|
|||||||
|
|
||||||
## gdb-peda
|
## gdb-peda
|
||||||
|
|
||||||
gdb-peda插件是
|
|
||||||
|
|
||||||
### 安装
|
### 安装
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -59,3 +57,24 @@ echo "source ~/peda/peda.py" >> ~/.gdbinit
|
|||||||
echo "DONE! debug your program with gdb and enjoy"
|
echo "DONE! debug your program with gdb and enjoy"
|
||||||
```
|
```
|
||||||
### peda命令
|
### peda命令
|
||||||
|
|
||||||
|
* `aslr` - 显示、设置gdb的ASLR
|
||||||
|
* `checksec` - 检查二进制文件的安全选项
|
||||||
|
* `dumpargs` - 在调用指令停止时显示传递给函数的参数
|
||||||
|
* `dumprop` - 在特定的内存范围显示ROP gadgets
|
||||||
|
* `elfheader` - 获取正在调试的ELF文件的头信息
|
||||||
|
* `elfsymbol` - 从ELF文件中获取没有调试信息的符号信息
|
||||||
|
* `lookup` - 搜索属于内存范围的地址的所有地址/引用
|
||||||
|
* `patch` - 使用字符串/十六进制字符串/整形数
|
||||||
|
* `pattern` -
|
||||||
|
* `procinfo` -
|
||||||
|
* `pshow` -
|
||||||
|
* `pset` -
|
||||||
|
* `readelf` -
|
||||||
|
* `ropgadget` -
|
||||||
|
* `ropsearch` -
|
||||||
|
* `searchmem|find` -
|
||||||
|
* `shellcode` -
|
||||||
|
* `skeleton` -
|
||||||
|
* `vmmap` -
|
||||||
|
* `xormem` -
|
@ -1,9 +1,9 @@
|
|||||||
# 2.6 Pwntools
|
# 2.6 Pwntools
|
||||||
|
|
||||||
* [安装](#安装)
|
* [安装](#安装)
|
||||||
* [pwntools的使用](#pwntools的使用)
|
* [使用pwntools](#使用pwntools)
|
||||||
|
|
||||||
pwntools是一个CTF框架和漏洞利用开发库,用Python开发,由rapid设计,旨在让使用者==简单快速的编写exploit。包含了本地执行、远程连接读写、shellcode生成、ROP链的构建、ELF解析、符号泄露众多强大功能。
|
pwntools是一个CTF框架和漏洞利用开发库,用Python开发,由rapid设计,旨在让使用者简单快速的编写exp脚本。包含了本地执行、远程连接读写、shellcode生成、ROP链的构建、ELF解析、符号泄露众多强大功能。
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
@ -17,8 +17,6 @@ pwntools是一个CTF框架和漏洞利用开发库,用Python开发,由rapid
|
|||||||
sudo apt-get install binutils-arm-linux-gnu
|
sudo apt-get install binutils-arm-linux-gnu
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2. 安装capstone:
|
2. 安装capstone:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -28,8 +26,6 @@ pwntools是一个CTF框架和漏洞利用开发库,用Python开发,由rapid
|
|||||||
sudo make install
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3. 安装pwntools:
|
3. 安装pwntools:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -45,297 +41,5 @@ pwntools是一个CTF框架和漏洞利用开发库,用Python开发,由rapid
|
|||||||
'1\xc0'
|
'1\xc0'
|
||||||
```
|
```
|
||||||
|
|
||||||
## pwntools的使用
|
## 使用pwntools
|
||||||
|
|
||||||
常用的模块有下面几个:
|
|
||||||
|
|
||||||
* asm:汇编与反汇编
|
|
||||||
* dynelf:用于远程符号泄露,需要提供leak方法
|
|
||||||
* lf:对elf文件进行操作
|
|
||||||
* gdb:配合gdb进行调试
|
|
||||||
* memleak:用于内存泄漏
|
|
||||||
* hellcraft: shellcode的生成器
|
|
||||||
* tubes:包括tubes: 包括tubes.sock, tubes.process, tubes.ssh, tubes.serialtube,分别适用于不同场景的PIPE
|
|
||||||
* utils:一些实用的小功能,例如CRC计算,cyclic pattern等
|
|
||||||
|
|
||||||
**Tubes读写接口:**
|
|
||||||
|
|
||||||
对于一次攻击而言前提就是与目标服务器或者程序进行交互,这里就可以用remote(address,port)产生一个远程的socket然后就可以读写了。
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> conn = remote('ftp.debian.org',21)
|
|
||||||
>>> conn.recvline()
|
|
||||||
'220 ...'
|
|
||||||
>>> conn.send('USER anonymous\r\n')
|
|
||||||
>>> conn.recvuntil(' ',drop = True)
|
|
||||||
'331'
|
|
||||||
>>> conn.recvline()
|
|
||||||
'Please specify the password.\r\n'
|
|
||||||
>>> conn.close()123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
同样的,使用process可以打开一个本地程序并进行交互
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> sh = process('/bin/sh')
|
|
||||||
>>> sh.sendline('sleep 3; echo hello world!;')
|
|
||||||
>>> sh.recvline(timeout=1)
|
|
||||||
''
|
|
||||||
>>> sh.recvline(timeout=5)
|
|
||||||
'hello world!\n'
|
|
||||||
>>> sh.close1234567
|
|
||||||
```
|
|
||||||
|
|
||||||
同时,也可以使用listen来开启一个本地的监听端口
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> l = listen()
|
|
||||||
>>> r = remote('localhost', l.lport)
|
|
||||||
>>> c = l.wait_for_connection()
|
|
||||||
>>> r.send('hello')
|
|
||||||
>>> c.recv()
|
|
||||||
'hello'123456
|
|
||||||
```
|
|
||||||
|
|
||||||
无论哪种PIPE都是继承tube而来,可以用于读写函数主要有:
|
|
||||||
|
|
||||||
* interactive() : 直接进行交互,相当于回到shell的模式,在取得shell之后使用
|
|
||||||
* ecv(numb = 4096,timeout = default):接收指定字节
|
|
||||||
* ecvall() : 一直接收知道EOF
|
|
||||||
* ecvline(keepends = True): 接收一行,keepends为是否保留行尾的\n,默认为Ture
|
|
||||||
* ecvuntil((delims,drop=False):一直读到delims的pattern出现为止
|
|
||||||
* ecvrepeat(timeout=default): 持续接收知道EOF或者timeout
|
|
||||||
* end(data) :发送数据
|
|
||||||
* endline(data) : 发送一行数据,相当于在数据末尾加\n
|
|
||||||
|
|
||||||
**汇编与反汇编:**
|
|
||||||
|
|
||||||
> <http://docs.pwntools.com/en/stable/asm.html?highlight=asm>
|
|
||||||
|
|
||||||
使用asm来进行汇编
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> asm('mov eax, 0')
|
|
||||||
'\xb8\x00\x00\x00\x00'
|
|
||||||
>>> asm('mov eax, SYS_execve')
|
|
||||||
'\xb8\x0b\x00\x00\x00'
|
|
||||||
>>> asm(shellcraft.nop())
|
|
||||||
'\x90'123456
|
|
||||||
```
|
|
||||||
|
|
||||||
可以使用context来指定cpu类型以及操作系统
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> context.arch = 'i386'
|
|
||||||
>>> context.os = 'linux'
|
|
||||||
>>> context.endian = 'little'
|
|
||||||
>>> context.word_size = 321234
|
|
||||||
```
|
|
||||||
|
|
||||||
注意:Any arguments/properties that can be set on context
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> asm("mov eax, SYS_select", arch = 'i386', os = 'freebsd')
|
|
||||||
'\xb8]\x00\x00\x00'
|
|
||||||
>>> asm("mov eax, SYS_select", arch = 'amd64', os = 'linux')
|
|
||||||
'\xb8\x17\x00\x00\x00'
|
|
||||||
>>> asm("mov rax, SYS_select", arch = 'amd64', os = 'linux')
|
|
||||||
'H\xc7\xc0\x17\x00\x00\x00'
|
|
||||||
>>> asm("mov r0, #SYS_select", arch = 'arm', os = 'linux', bits=32)
|
|
||||||
'R\x00\xa0\xe3'12345678
|
|
||||||
```
|
|
||||||
|
|
||||||
使用disasm进行反汇编(同样可以指定cpu类型)
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> print disasm('6a0258cd80ebf9'.decode('hex'))
|
|
||||||
0: 6a 02 push 0x2
|
|
||||||
2: 58 pop eax
|
|
||||||
3: cd 80 int 0x80
|
|
||||||
5: eb f9 jmp 0x0
|
|
||||||
>>> print disasm('b85d000000'.decode('hex'), arch = 'i386')
|
|
||||||
0: b8 5d 00 00 00 mov eax,0x5d
|
|
||||||
>>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0)
|
|
||||||
0: mov eax,0x5d
|
|
||||||
>>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0, offset = 0)
|
|
||||||
mov eax,0x5d
|
|
||||||
>>> print disasm('b817000000'.decode('hex'), arch = 'amd64')
|
|
||||||
0: b8 17 00 00 00 mov eax,0x17
|
|
||||||
>>> print disasm('48c7c017000000'.decode('hex'), arch = 'amd64')
|
|
||||||
0: 48 c7 c0 17 00 00 00 mov rax,0x17
|
|
||||||
>>> print disasm('04001fe552009000'.decode('hex'), arch = 'arm')
|
|
||||||
0: e51f0004 ldr r0, [pc, #-4] ; 0x4
|
|
||||||
4: 00900052 addseq r0, r0, r2, asr r0
|
|
||||||
>>> print disasm('4ff00500'.decode('hex'), arch = 'thumb', bits=32)
|
|
||||||
0: f04f 0005 mov.w r0, #51234567891011121314151617181920
|
|
||||||
```
|
|
||||||
|
|
||||||
**shellcode生成器:**
|
|
||||||
|
|
||||||
> <http://docs.pwntools.com/en/stable/shellcraft.html?highlight=shellcraft>
|
|
||||||
|
|
||||||
使用shellcraft可以生成对应[架构](http://lib.csdn.net/base/architecture)的shellcode代码,直接使用链式调用的方法就可以得到
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> print shellcraft.i386.nop().strip('\n')
|
|
||||||
nop
|
|
||||||
>>> print shellcraft.i386.linux.sh()
|
|
||||||
/*push '/bin///sh\x00'*/
|
|
||||||
push 0x68
|
|
||||||
push 0x732f2f2f
|
|
||||||
push 0x6e69622f1234567
|
|
||||||
```
|
|
||||||
|
|
||||||
如上所示,如果需要在64位的Linux上执行/bin/sh 就可以使用shellcraft.amd64.linux.sh(),配合asm函数就能够得到最终的payload了。
|
|
||||||
|
|
||||||
除了直接执行sh之外,还可以进行其他的一些常用操作如提权、反向链接等。
|
|
||||||
|
|
||||||
**ELF文件操作:**
|
|
||||||
|
|
||||||
> <http://docs.pwntools.com/en/stable/elf.html?highlight=elf#module-pwnlib.elf>
|
|
||||||
|
|
||||||
这个还是挺实用的,在进行elf文件逆向的时候,总是需要对各个符号的地址进行分析,elf模块提供了一种便捷的方法能够==迅速得到文件内函数的地址,plt位置以及got表的位置。==
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> e = ELF('/bin/cat')
|
|
||||||
>>> print hex(e.address) # 文件装载的基地址
|
|
||||||
0x400000
|
|
||||||
>>> print hex(e.symbols['write']) # 函数地址
|
|
||||||
0x401680
|
|
||||||
>>> print hex(e.got['write']) # GOT表的地址
|
|
||||||
0x60b070
|
|
||||||
>>> print hex(e.plt['write']) # PLT的地址
|
|
||||||
0x401680123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
同样,也可以打开一个libc.so来解析其中system的位置:
|
|
||||||
|
|
||||||
甚至可以修改一个ELF的代码
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> e = ELF('/bin/cat')
|
|
||||||
>>> e.read(e.address+1, 3)
|
|
||||||
'ELF'
|
|
||||||
>>> e.asm(e.address, 'ret')
|
|
||||||
>>> e.save('/tmp/quiet-cat')
|
|
||||||
>>> disasm(file('/tmp/quiet-cat','rb').read(1))
|
|
||||||
' 0: c3 ret'1234567
|
|
||||||
```
|
|
||||||
|
|
||||||
下面是一些可用的函数:
|
|
||||||
|
|
||||||
- asm(address, assembly) : 在指定地址进行汇编
|
|
||||||
- bss(offset) : 返回bss段的位置,offset是偏移值
|
|
||||||
- checksec() : 对elf进行一些安全保护检查,例如NX, PIE等。
|
|
||||||
- disasm(address, n_bytes) : 在指定位置进行n_bytes个字节的反汇编
|
|
||||||
- offset_to_vaddr(offset) : 将文件中的偏移offset转换成虚拟地址VMA
|
|
||||||
- vaddr_to_offset(address) : 与上面的函数作用相反
|
|
||||||
- read(address, count) : 在address(VMA)位置读取count个字节
|
|
||||||
- write(address, data) : 在address(VMA)位置写入data
|
|
||||||
- section(name) : dump出指定section的数据
|
|
||||||
|
|
||||||
**ROP链生成器:**
|
|
||||||
|
|
||||||
先简单回顾一下ROP的原理,由于NX开启不能在栈上执行shellcode,我们可以在栈上布置一系列的返回地址与参数,这样可以进行多次的函数调用,通过函数尾部的ret语句控制程序的流程,而用程序中的一些pop/ret的代码块(称之为gadget)来平衡堆栈。其完成的事情无非就是放上/bin/sh,覆盖程序中某个函数的GOT为system的,然后ret到那个函数的plt就可以触发`system('/bin/sh')`。由于是利用ret指令的exploit,所以叫Return-Oriented Programming。(如果没有开启ASLR,可以直接使用ret2libc技术)
|
|
||||||
|
|
||||||
好,这样来看,这种技术的难点自然就是如何在栈上布置返回地址以及函数参数了。而ROP模块的作用,就是自动地寻找程序里的gadget,自动在栈上部署对应的参数。
|
|
||||||
|
|
||||||
```python
|
|
||||||
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)
|
|
||||||
# '\xfc\x82\x04\x08\xef\xbe\xad\xde\x00\x00\x00\x00\xa8\x96\x04\x08'12345678910
|
|
||||||
```
|
|
||||||
|
|
||||||
使用ROP(elf)来产生一个rop的对象,这时rop链还是空的,需要在其中添加函数
|
|
||||||
|
|
||||||
因为ROP对象实现了**getattr**的功能,可以直接通过func call的形式来添加函数,rop.read(0, elf.bss(0x80))实际相当于rop.call(‘read’, (0, elf.bss(0x80)))。
|
|
||||||
|
|
||||||
通过多次添加函数调用,最后使用str将整个rop chain dump出来就可以了。
|
|
||||||
|
|
||||||
- call(resolvable, arguments=()) : 添加一个调用,resolvable可以是一个符号,也可以是一个int型地址,注意后面的参数必须是元组否则会报错,即使只有一个参数也要写成元组的形式(在后面加上一个逗号)
|
|
||||||
- chain() : 返回当前的字节序列,即payload
|
|
||||||
- dump() : 直观地展示出当前的rop chain
|
|
||||||
- raw() : 在rop chain中加上一个整数或字符串
|
|
||||||
- search(move=0, regs=None, order=’size’) : 按特定条件搜索gadget,没仔细研究过
|
|
||||||
- unresolve(value) : 给出一个地址,反解析出符号
|
|
||||||
|
|
||||||
**其他:**
|
|
||||||
|
|
||||||
对于整数的pack与数据的unpack,可以使用p32,p64,u32,u64这些函数,分别对应着32位和64位的整数。
|
|
||||||
|
|
||||||
另外,在utils工具中比较常用的就是可以使用cyclic pattern来找到return address的位置,这个功能在gbd peda中也是有的,这里就不过多介绍了。
|
|
||||||
|
|
||||||
**GDB调试:**
|
|
||||||
|
|
||||||
对于elf文件来说,可能有时需要我们进行一些动态调试工作,这个时候级需要用到gdb,pwntools的gdb模块也提供了这方面的支持。
|
|
||||||
|
|
||||||
其中最常用的还是attach函数,在指定process之后可以attach上去调试,配合proc模块就可以得到进程的Pid非常方便。
|
|
||||||
|
|
||||||
但是比较麻烦的是在实现上,attach函数需要开启一个新的terminal,这个terminal的类型必须使用环境变量或者context对象来指定。研究了一番源码之后,找到了解决方案。
|
|
||||||
|
|
||||||
```python
|
|
||||||
s = process('./pwnme')
|
|
||||||
context.terminal = ['gnome-terminal','-x','sh','-c']
|
|
||||||
gdb.attach(proc.pidof(s)[0])123
|
|
||||||
```
|
|
||||||
|
|
||||||
proc.pidof(s)[0]能够取出process的id,然后attach上去。context.terminal制定的是终端类型和参数,我用的是gnome-terminal可以这样写,这样运行后会自动打开一个新的gnome-terminal并在里面启动gdb并自动断下来,这样就可以调试了。
|
|
||||||
|
|
||||||
**DynELF 符号leak**
|
|
||||||
|
|
||||||
> <http://docs.pwntools.com/en/stable/dynelf.html>
|
|
||||||
|
|
||||||
相当好用的一个工具,给出一个函数句柄,可以解析任意符号的位置。这个函数的功能是:==输入任意一个address,输出这个address中的data(至少1byte)。==
|
|
||||||
|
|
||||||
官网给的例子:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Assume a process or remote connection
|
|
||||||
p = process('./pwnme')
|
|
||||||
|
|
||||||
# Declare a function that takes a single address, and
|
|
||||||
# leaks at least one byte at that address.
|
|
||||||
def leak(address):
|
|
||||||
data = p.read(address, 4)
|
|
||||||
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
|
|
||||||
return data
|
|
||||||
|
|
||||||
# For the sake of this example, let's say that we
|
|
||||||
# have any of these pointers. One is a pointer into
|
|
||||||
# the target binary, the other two are pointers into libc
|
|
||||||
main = 0xfeedf4ce
|
|
||||||
libc = 0xdeadb000
|
|
||||||
system = 0xdeadbeef
|
|
||||||
|
|
||||||
# With our leaker, and a pointer into our target binary,
|
|
||||||
# we can resolve the address of anything.
|
|
||||||
#
|
|
||||||
# We do not actually need to have a copy of the target
|
|
||||||
# binary for this to work.
|
|
||||||
d = DynELF(leak, main)
|
|
||||||
assert d.lookup(None, 'libc') == libc #libc的基址
|
|
||||||
assert d.lookup('system', 'libc') == system #system的地址
|
|
||||||
|
|
||||||
# However, if we *do* have a copy of the target binary,
|
|
||||||
# we can speed up some of the steps.
|
|
||||||
# 指定一份elf的副本可以加速查找过程
|
|
||||||
d = DynELF(leak, main, elf=ELF('./pwnme'))
|
|
||||||
assert d.lookup(None, 'libc') == libc
|
|
||||||
assert d.lookup('system', 'libc') == system
|
|
||||||
|
|
||||||
# Alternately, we can resolve symbols inside another library,
|
|
||||||
# given a pointer into it.
|
|
||||||
d = DynELF(leak, libc + 0x1234)
|
|
||||||
assert d.lookup('system') == system12345678910111213141516171819202122232425262728293031323334353637
|
|
||||||
```
|
|
||||||
|
|
||||||
这个例子当然没有实际意义,在应用中我们可以在leak函数中布置rop链,使用write函数leak出一个address的地址,然后返回。接着就可以使用d.lookup函数查找符号了,通常我们都是需要找system的符号。
|
|
||||||
|
|
||||||
还有更多的Pwntools的功能,待以后实际操作过程中再一一学习。
|
|
Loading…
Reference in New Issue
Block a user