Flag Shop
解题思路

刚打开是这样。界面里有uid、jkl、flag price.点击Buy flag按钮

jkl不够,我们需要有足够的jkl才能买到flag。
点击reset按钮会重置uid、jkl个数,没什么特别的用处。
点击work,注意到jkl会随机增加,但是增加个数在10个以内,因此就算是用脚本跑也要跑很久很久很久很久。所以另找办法。
我们看看网页的源代码:
function work() { |
可以大致看出来工作流程,先获取信息,失败后请求auth,然后会有个jwt token。再每次work后都会输入生成新的cookie。
让我们看看cookie,再把他丢到jwt.io里解码一下:

因此我们想到,可以在jwt中修改jkl,使它达到能够购买flag需要的数量,再把构造好的cookie替换到题目里,就能成功买到flag了,买成功之后就会返回一个新的cookie,再解码应该就能够得到flag了。
但伪造cookie需要得到SIGNATURE,因此我们得找到源代码才能看出SIGNATURE是怎么来的。
找到源码
扫目录发现有个robots.txt
,让我们进去看看

再进/filebak
里面去看看,发现是源码,是用ruby写的。

让我们来看关键代码:
get "/work" do |
从ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>")
看出直接用了ERB模版,并且还将可控参数name直接拼接进去了,以alert的方式回显在页面上。因此我们可以在可控参数name处传入一些构造过的参数。
ERB模版注入
ERB代表嵌入式Ruby,用于在模板中插入Ruby变量,例如HTML和YAML。 ERB是一个Ruby类,它接受文本,并评估和替换ERB标记包围的Ruby代码。它的模版格式为:
<%= 7*7 %> |
从以上我们了解到<%=
语法可以用来执行Ruby语句,并会尝试将结果转换为字符串,以附在最终的结果文本中。并且此处限定了只能输入7个字符以内。
并且ERB注入跟sql注入等的原理很相似,都是插入的代码被执行了。因此我们这里就可以通过name传入代码,执行结果就会alert出执行结果。例如name=<%=1%>
,并且需要先url编码一下,防止特殊字符的影响。

解题
上上上一步说到,我们找源码的原因是要得到伪造cookie所需要的签名。让我们来看看源码中关于cookie的处理代码。
get "/work" do |
可以看出,cookie是需要ENV["SECRET"]
作为签名得到的。因此我们如果能得到每次work后的ENV["SECRET"]
,就可以伪造cookie了!
这段代码的后半部分说明了,在ERB模版渲染以前有一个正则匹配。如果SECRET参数存在的话,就对其进行匹配,并用传入的值与ENV["SECRET"]
进行匹配,匹配成功就会输出FLAG。如果我们不传SECRET,这样括号里的匹配就不进行,只进行括号外的 ENV[“SECRET”] 的匹配。
- ruby全局变量
ruby的全局变量以$
开头,例如: $x
,$y
。全局变量可以在程序的任何地方加以引用。全局变量无需变量声明。引用尚未初始化的全局变量时,其值为nil。并且ruby有内置的全局变量表,在这里。

这里既然有匹配,那说明我们就可以用全局变量读出来了,也就是可以用上图的符号来读取匹配前的内容(即ENV["SECRET"]
)
因此我们可以构造,再进行url编码后传入。
name=<%=$'%>&do=<%=$'%> is working&SECRET= |

得到SECRET了,再把他拿到jwt.io里进行编码,并把jkl数量设置为大于flag的购买数量。

再把新的cookie替换到题目中,显示购买成功了。

post "/shop" do |
在关于shop的源码中可以看到,购买成功后,flag将会jwt编码。因此我们只需要把返回的新cookie拿去解码就能得到flag了!
待补充的题
这个比赛的剩下两个题都太难了qaq我看赵师傅的wp都看不懂,先做能够理解一点的题之后再来看这些难题8。