文章目录
exp编写小技巧
1.代码对齐
exp中填写地址时要注意代码长度的填充
2. 填充为指定长度
3. 链接远程服务器或者链接本地文件
# 远程
r = remote('目的IP或目标URL',目标端口号)
# 本地
r = process('./文件名')
4. 格式转换
32位程序对应p32 / u32,64位对应p64 / u64
>>> p32(0x78739736)
b'6\x97sx'
>>> hex(u32('6\x97sx'))
'0x78739736'
5. 环境变量
context
是pwntools用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。
context(os='linux', arch='amd64', log_level='debug') # 如果不设置默认是32位
这句话的意思是:
- os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
- arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
- log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。
6. 获得汇编指令的机器代码
>>> asm('mov eax, 0')
b'\xb8\x00\x00\x00\x00'
因为asm和架构有关系,因此使用前一定要设定好相关的context架构参数
7. 把权限交给用户
r.interactive()
8. shellcode
pwntools提供简单的shellcode支持,使用shellcraft()可以得到简单的shellcode
它的返回值是汇编指令
>>> print(shellcraft.sh())
/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp
/* push argument array ['sh\x00'] */
/* push 'sh\x00\x00' */
push 0x1010101
xor dword ptr [esp], 0x1016972
xor ecx, ecx
push ecx /* null terminate */
push 4
pop ecx
add ecx, esp
push ecx /* 'sh\x00' */
mov ecx, esp
xor edx, edx
/* call execve() */
push SYS_execve /* 0xb */
pop eax
int 0x80
使用时,需要先转换为机器码
shellcode = asm(shellcraft.sh())
pwntools提供许多的shellcode,其他的见:https://docs.pwntools.com/en/stable/shellcraft/amd64.html
9. ELF 操作ELF文件的工具
elf = ELF('pwn')
hex(elf.address)
hex(elf.symbols['write']
hex(elf.got['write'])
hex(elf.plt['write'])
##10. 长度测算cyclic
cyclic 需要的长度
cyclic -l 异常的地点(这里异常的地点给定4字节)
11.接收数据
recv(numb = 字节大小,timeout = default) # 接收指定字节数
p.recvn(N) # 接受 N(数字) 字符
recall() # 一直接收知道达到文件EOF
recvline(keepends = True) # 接收一行,keepends为是否保留行尾的\n
p.recvlines(N) # 接收 N(数字) 行输出
recvuntil(delims,drop = False) # 一直读到delims的pattern出现为止
recvrepeat(timeout=default) # 持续接收直到EOF或timeout
p.interactive() # 直接进行交互,相当于回到shell的模式,一般在取得shell之后使用
12.发送数据
p.send(payload) # 发送 payload
p.sendline(payload) # 发送 payload,并进行换行(末尾\n)
p.sendafter(some_string, payload) # 接收到 some_string 后, 发送你的 payload
p.sendlineafter(some_string, payload) # 接收到 some_string 后, 发送你的 payload,加个换行
13.填充字符,字符串向右/向左填充
填充时,需要保证填充的字符和原字符是一个类型
>>> type(str)
<class 'str'>
>>> tmp = b'A'
>>> print(tmp.ljust(10, b'B'))
b'ABBBBBBBBB'
>>> tmp2 = "A"
>>> type(tmp2)
<class 'str'>
>>> print(tmp2.ljust(10, 'B'))
ABBBBBBBBB
14.寻找ROP
可以使用ROPgadget工具寻找可用的ROP,使用模板如下:
ROPgadget --binary "param1" --only 'pop|ret' | grep 'param2'
# param1: 文件名
# param2: 要搜索的相关寄存器
使用样例如下:
$ ROPgadget --binary "./rop" --only 'pop|ret' | grep 'eax'
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
$ ROPgadget --binary "./rop" --only 'int'
Gadgets information
===================&