前面在总结攻防世界web系列的WP,然后碰到了这题,是16年CSAW CTF Qual的题,原本以为这里的题都会比较基础,但是没想到第二页就碰到了这样一个题,感觉通过这个题目还是能够学到点东西的。
解题过程
flag1
首先打开题目,是一个论坛,有登录、注册、发表、评论等操作,最开始考虑是否能够进行注入但是发现没什么用,扫目录也找不到什么可以利用的文件,源码中也没有提示。
于是考虑多点开几个页面,对于源码和url等多观察,发现在查看别人的post时,url为"http://111.198.29.45:49606/post.wtf?post=NpO4g#1 "形式,是否post传输的是一个目录参数,通过目录参数来获取存储的post数据?这也是现在唯一找到的一个可控的参数。
因此考虑修改post参数,看看是否会有什么变化。最后发现,如果输入"…/",则会出现目录遍历,可以查看到网站源码。在搜索出来的源码中搜索,可以发现这样的代码。
对代码进行美化,结果如下:
<html > <head > <link rel ="stylesheet" type ="text/css" href ="/css/std.css" > <title > </title > </head > <body > $ if contains 'user' ${!URL_PARAMS[@]} & & file_exists "users/${URL_PARAMS['user']}" $ then $ local username=$(head -n 1 users/${URL_PARAMS['user']}); $ echo " <h3 > ${username}'s posts: </h3 > "; $ echo " <ol > <li style ="list-style: none" > "; $ get_users_posts "${username}" | while read -r post; do $ post_slug=$(awk -F/ '{print $2 "#" $3}' < < < "${post}"); $ echo ""; $ done $ echo " </li > <li > <a href ="/" /post.wtf?post=${post_slug}/" " > $(nth_line 2 "${post}" | htmlentities)</a > </li > </ol > "; $ if is_logged_in & & [[ "${COOKIES['USERNAME']}" = 'admin' ]] & & [[ ${username} = 'admin' ]] $ then $ get_flag1 $ fi $ fi </body > </html >
主要在于这里:
$ if is_logged_in & & [[ "${COOKIES['USERNAME']}" = 'admin' ]] & & [[ ${username} = 'admin' ]] $ then $ get_flag1 $ fi $ fi
代码的意思为,将username变为admin,并且cookies为admin则可以获取flag1(flag不止一个部分???)
先抓取一个自己创建的用户的包看看:
可以看到,这里面cookie中有一个token,另外username可以直接修改为admin,因此需要获取到admin的token
观察到token貌似为base64编码,但是尝试后并不是…只能看看是否有哪里藏有这一信息,回到刚刚的源码中,查找一些是否有user信息,发现有一个users,将其放到post参数中可以得到admin的token。
得到了admin的token,于是抓包改包,将自己账户设置为admin,多查看几个页面,发现在打开"Profile"页时可以得到flag的一部分(果然flag是分部分的)
flag2
下面考虑如何找到flag的其他部分,这里继续审计刚刚查找出来的源码,看是否能够找到一些其他可以利用的地方,获取flag的其他部分。
这里有两个技巧,一个是看常用的一些关键路径,一个是直接关键词搜索"exec"看看是否有可以命令注入执行的函数,发现有一个"#!/usr/local/bin/bash"文件,对代码进行审计,得到源码如下
max_page_include_depth=64 page_include_depth=0 function include_page { local pathname=$1 local cmd= [[ ${pathname(-4)} = '.wtf' ]]; local can_execute=$; page_include_depth=$(($page_include_depth +1)) if [[ $page_include_depth -lt $max_page_include_depth ]] then local line; while read -r line; do [[ $ = ${line01} && ${can_execute} = 0 ]]; is_script=$; if [[ $is_script = 0 ]] then cmd+=$'n' ${line#$} ; else if [[ -n $cmd ]] then eval $cmd log Error during execution of ${cmd} ; cmd= fi echo $line fi done ${pathname} else echo pMax include depth exceeded! pfi }
审计源码,可以看出,改代码可以上传wtf文件,并且可以执行这一文件,于是尝试上传wtf文件或者注入wtf命令以达到命令执行获取flag的目的,但是这里并没有给出如何进行上传,需要继续审计代码。看到下面的代码:
function reply { local post_id=$1 ; local username=$2 ; local text=$3 ; local hashed=$(hash_username "${username} " ); curr_id=$(for d in posts/${post_id} /*; do basename $d ; done | sort -n | tail -n 1); next_reply_id=$(awk '{print $1+1}' <<< "${curr_id} " ); next_file=(posts/${post_id} /${next_reply_id} ); echo "${username} " > "${next_file} " ; echo "RE: $(nth_line 2 < "posts/${post_id}/1") " >> "${next_file} " ; echo "${text} " >> "${next_file} " ; echo "${post_id} /${next_reply_id} " >> "users_lookup/${hashed} /posts" ; }
这里发现可以通过post上传文件,另外这里在发post的时候,“echo “${username}” > “${next_file}”;” 这一句会将用户名写入到wtf文件中,于是想到可以将命令构造为用户名注入到wtf文件中,由服务器执行即可获取flag,这里需要注意wtf.sh只运行文件扩展名为.wtf的脚本和前缀为’$'的行。
需要先构造一个payload查询flag文件名,这里创建用户为 ${find,/,-iname,get_flag2} ,因为上面是reply函数,所以抓取reply的包进行改包。另外的话,在改包时将post修改为上传的wtf文件,这里注意需要添加%09,否则不会将这个文件进行解析,而是将其看成是目录
抓包改包发送后,访问wtf文件,可以看到命令执行后的回显
回显中可以看到get_flag2的路径,于是创建用户名为 $/usr/bin/get_flag ,重复上述步骤即可得flag2