跳过正文
Python Pwntools的基础使用方法
  1. 文章合集/

Python Pwntools的基础使用方法

·2056 字·
KioroshiAki
作者
KioroshiAki
Burning Love !!!!!
目录

大概一个半月之前,我打算开始学pwn,那时我还是啥都不知道的小菜。花了半天时间配了个WSL,找了一堆视频和文章,有的上来开始讲ELF,有的上来开始讲栈溢出,虽然这些在pwn中确实很基础,但我看了半天感觉确实也很离谱。

全 都 看 不 懂 !

ALL_FxxKING_SUCK(nope)

后来找了个大佬求带,大佬当时只给我讲了一个题,那道题不需要各种漏洞,只需要做一个“简单的口算”,在限定时间内求出两个几千左右的随机数的积,我顿感震惊:这就是最基础的二进制安全吗?防止开挂破坏玩家生态??

所以说,作为学了一个月还是没有啥成就的小菜,要来写(shui)第一篇blog,还必须要有干货,写什么?那就写python pwntools怎么用如何写一个神奇的口算脚本

基础使用方法
#

首先导入pwntools:

from pwn import *

我们的主要目的是连接程序进行交互,但是建立连接之前,咱们也可以进行一些设置。

设置context
#

这里可以设置一些背景信息。你可以告诉脚本使用的架构,操作系统,日志详细程度等详细信息,一般情况下是可以不设置的,但是设置了可能方便某些模块的使用,或者获得更详细的调试信息。

# 可以这样
context(xx = "xx", yy = "yy")
# 也可以这样
context.xx = "xx"
context.yy = "yy"

# 具体设置的参数:
context.arch = "amd64"  # 架构,有i386,amd64等
context.os = "linux"  # 操作系统,一般都是linux
context.log_level = "debug"  # 日志详细程度,选择debug之后每次接收或者发送都有详细的日志

设置目标libc,ELF
#

你可以设置ELF和libc,这样可以让脚本帮你找一些gadgets的位置,比如标准库函数的偏移地址。

xx = ELF("./xx")  #指定一个ELF

# 接下来你就可以对这个ELF做一些操作,比如:
system_offset = xx.symbols['system'] # 找一些函数的地址,如果开了地址随机化就是偏移地址
binsh_offset = xx.search(b"/bin/sh") # 搜一些字符串的地址,开了随机化同上
puts_got = xx.got['puts'] # 找一个函数的got表地址,同上
puts_plt = xx.plt['puts'] # 找一个函数的plt表地址,同上

xx.address = 0x114514 # 设置这个ELF的基址,之后搜索到的地址都是加上基址之后的实际地址

启动程序
#

接下来就可以启动程序,开始操作了。

xx = process("./xx") # 开一个本地程序
xx = connect("0.0.0.0", 11111) # 连接一个在线环境

gdb.attach(xx) # gdb附加调试
gdb.attach(xx, '''
    b 0x114514
    continue
''') # 还能顺带给gdb发点指令!

基础交互
#

你可以对程序做一些交互,比如发送和接收。

# 接收
xx.recv(n) # 收n个字节
xx.recvline() # 收一行
xx.recvuntil(b'xx') # 收到xx为止
xx.recvall() # 一直收

a = xx.recv(114514) # 还能把收到的字符串赋给a!
a = xx.recv(16).strip() # 移除两端的空白字符
a = int(xx.recv(16), 10) # 如果收到的字符串像个数字,可以把他按特定进制转换成整数

xx.clean() # 还能把收到的扔了!



# 发送
xx.send(b'xx') # 发送xx,不带回车
xx.sendline(b'xx') # 带回车
xx.sendafter(b'xx', b'yy') # 收到xx后发yy,不带回车
xx.sendlineafter(b'xx', b'yy') # 收到xx后发yy,带回车

payload = b'xx' + b'yy'
xx.send(payload) # 构造好了再发

payload = cyclic(100)
xx.send(payload) # 发垃圾

payload = p32(0x64636261, endian = 'little') # 以指定字节序(不指定默认小端序)将整数打包为特定位数的字节串,不足高位补0
# payload = b'abcd'
payload = u32(b'abcd', endian = 'little') # 以指定字节序(不指定同上)将字节串解包为特定位数的整数,长度必须为32bits
# payload = 0x64636261

a = 114514
xx.send(str(a).encode()) # 将数字转化为字符串编码输出,注意要用encode编码,不编码默认用ascii编码,可能发送的不是你想发送的东西



# 其他
a = 2
log.info("xx") # 可以添加一些醒目的调试信息
log.success("a = " + str(a)) # 成功信息,注意后面加的只能是字符串
# 还有警告warn,错误error,等待waitfor等信息

xx.interactive() # 由你亲自上阵进行交互!一般用来验证是否成功getshell,或者拿flag

实战演练
#

现在我们就来尝试做一个口算脚本!

例题:极客大挑战2024 - 简单的签到

拿到文件,拖入ida,主要的函数如下:

int math1()
{
  unsigned int v0; // eax
  int v2; // [rsp+10h] [rbp-10h]
  int v3; // [rsp+14h] [rbp-Ch]

  v0 = time(0LL);
  srand(v0);
  v3 = rand() % 10000 + 1;
  v2 = rand() % 10000 + 1;
  printf("%d * %d = ", v3, v2);
  if ( (unsigned int)get_input_with_timeout() != v2 * v3 )
  {
    printf("Incorrect. The correct answer was %d. Exiting...\n", v2 * v3);
    exit(1);
  }
  puts("Correct! Opening shell...");
  return system("/bin/sh");
}

这个函数会随机两个一万以内的数,要求你在限定时间内输入这两个数的积。

我们只要收到数字然后相乘就可以了,但是他发送的是一个乘法,不是单纯的两个数字,如何去掉其他字符呢?

这时就可以用上面提到的recvuntilstrip了!

写好的payload如下:

from pwn import *
# 我们也可以设置context的,但是这里不需要设置,懒了(跪
io = process("./main")

io.recvuntil(b'challenge.') # 接收欢迎信息
io.send(b'\n') # 按任意键继续

a = int(io.recvuntil(b' ').strip()) # 收到乘号前面的空格为止,去除空白
log.success("a = " + str(a)) # 实际上可以不加
io.recvuntil(b'* ') # 把乘号和后面的空格收了
b = int(io.recvuntil(b' ').strip()) # 收到等号前面的空格为止,去除空白
log.success("b = " + str(b))

r = a * b # 计算
log.success("a * b = " + str(r))

io.recvuntil(b'= ') # 把等号和后面的空格收了
io.sendline(str(r).encode()) # 编码输出

io.interactive() # 开启交互模式

先给拿到的附件里的ELF程序加上可执行权限,让脚本能打开程序,运行脚本。

chmod +x main
python3 1.py

等进入交互模式后,你就可以验证自己有没有成功getshell了。

# 本地可以用这个
whoami # 发送用户名

# 远程可以用这个
ls # 看看当前目录中的东西
cat flag # 如果有flag,直接读取

最后大概就是这样:

[+] Starting local process './main': pid 8358
[+] a = 9307
[+] b = 1667
[+] a * b = 15514769
[*] Switching to interactive mode
Correct! Opening shell...
$ whoami
kioroshiaki

总结
#

本篇文章讲述了一些python pwntools中的常见指令与语法。

看完最后的脚本,你会发现前面好多内容完全看不懂 / 用不到,不过这是为了水blog啊(被打

确实有很多内容暂时不会用到,但是这里就先把他们归类到一起,不用全部记住,以后用到了可以再查

第一篇blog就是这样,终于水完了(逃

通过邮件回复