代码审计之小众语言

Published: 2022年03月15日

In Vuln.

慢慢记,现在是草稿状态,不要看!!!!

shell

  1. shell需要过滤的字符有'"|<>()[]~?*{};&$\r\n`\在不需要转义的字符上无效,一个正确的转义方法是将参数用''包裹,并将所有'替换为'\''

  2. 对于空白符可尝试用${IFS},$IFS$9,%09 等方式绕过。

  3. 注意一些命令的参数,如find-exec {} \;-ok,-deleteawksystemprint等都可以执行命令
  4. 新学到的技巧,算数运算里的变量可控也可能RCE,见Shell Arithmetic Expansion and Evaluation Abuse

Python

沙盒逃逸

Python 沙盒绕过禁用import的情况下绕过python沙箱用python继承链搞事情...

反序列化

PyYalm和自身的序列化都可实现任意代码执行,存放于外部的pickle数据也是不安全的,如redis可能存在漏洞数据被改写,可参考Python反序列化漏洞的花式利用Python pickle 反序列化实例分析...

格式化串

Python格式化字符串漏洞(Django为例),Python的format若能控制格式化串则可泄漏任意数据,而f’’型若内容可控则可执行任意代码,不过后者出漏洞的情况应该很少。Python Web之flask session&格式化字符串漏洞。对此的防御当然是不让它可控最好,若实在要可控就上沙盒过滤吧

命令注入

Python内置的代码注入就是system/popen/subprocess/getoutput等方法,一般现代的代码会使用subprocess库,此时就看有没有用shell=True与列表传指令参数,其他若是拼接当然是看过滤是否可以绕过,shlex.quote(),可参考Python Command Execution

代码注入

eval/exec/timeit/execfile/assert/compile/input,一般eval可考虑用ast.literal_eval或json等函数代替

模板注入

模板注入一般会用到格式化串和沙箱逃逸的方法

目录穿越

有个经典的问题就是在目录拼接时,追加的路径以/字符:/开始将丢弃前面的目录,以此作为起始位置继续拼接

XXE

如果是defusedxml就当是很安全吧,毕竟下面这张图就是作者做的,其他的可以库可见下图:

image-20220721191343262

sax包从3.71默认禁用外部实体,或者使用xml_parser.setFeature(feature_external_ges, False)禁用;lxml需要手动XMLParser(resolve_entities=False, no_network=True)禁用;其他如图...

其他还可参考:[0] Pythonanquan,[1] 关于Python sec的一些简单的总结

之前还有两个有名的CRLF漏洞urllib header inject(CVE-2016-5699,CVE-2019-9740) 有时也要关注导入的库,特别不知名的库可能就写了漏洞

JS

基础

node可通过添加启动参数--inspect或发送SIGUSR1启用,它默认监听9229端口,远程调试可使用如下命令建立ssh隧道[1]:

ssh -L 9229:localhost:9229 <root>@<192.168.30.204>

对于EGG,它会有多个进程,若存在或可以安装egg-bin时可使用npm run debug命令开启调试[0],否则可以直接在想调试的进程上开调试,如调试工作进程可修改app_worker进程数为1后,直接在它上面开调试。

在多进程时,它会依次递增端口号在子进程上监听。

原型链污染

最出名的是原型链污染,js是基于原型的,在读写属性时若本地没有则会向上查找,可通过这种特性去修改原型链上的属性值,基于该原型的其他实例可能会使用该值,进而造成其他漏洞[2, 3],具体来说如果能出现这种a[x][y][.]*=z语句,我们能控制xyz即可进行污染,但是通常不会出现这种情况,而是出现在递归合并与点分路径解析中,如[5]曾经爆的命令执行漏洞。

当出现原型链污染时,需要根据具体应用分析可行的利用方式,如绕过授权或命令执行,通过AST工具[9]来执行代码....

命令注入

在使用child_process时,有三个命令执行函数,exec直接调sh -c最危险,execFilespawn直接调程序一般没问题,但是如果加了shell=true选项时也有问题[6]。

SQL注入

同,基本都是用参数化查询或者ORM因此出现问题概率是比较小的,node下常见的是node-mysqlSequelize,这里简单记录下监视点的位置:

node_modules/sequelize/lib/dialects/mysql/query.js:run

同,有些语法无法用参数化,如like此时开发人员可能会做一些危险的操作,如用Sequelizeliteral方法,此时如果没有正确的过滤就可能存在安全问题。

记下一些库审计点:

1.pg-promise:是pg库,通常的写法都是由它的格式化函数处理没问题,但若参数直接拼接到语句里,使用:raw/^:value/#时会有问题,前者不会进行任何转义,后者会正确转义但不会对字符串值添加引号。

反序列化

另外它也逃不脱反序列化影响,而且它的反序列化是最容易利用的,既不需要利用链也不需要构造复杂的payload,见[4]。

XXE

一般不会有问题,但是若libxmljs使用了{noent: true}选项则存在问题,sax-js不会处理自定义的DTD因此没问题,而xml2js依赖于sax-js因此也不受影响。

模板注入

一例[10]

其他

Buffer(Num)会存在未清空数据...

参考

[0] Node.js 和 Egg.js 项目远程调试 -- HANK WEI (2020)

[1] NODEJS官网:调试指南

[2] 深入理解 JavaScript Prototype 污染攻击

[3] Prototype pollution attack

[4] 利用 Node.js 反序列化漏洞远程执行代码 -- Ajin Abraham, Holic[译] (2017)

[5] 例: Prototype Pollution in Kibana例: Remote Code Execution via Prototype Pollution in Blitz.js

[6] Command Injection in Node.js -- NF997, gkouziik (2020)

[7] From Markdown to RCE in Atom

[8] Modern Alchemy: Turning XSS into RCE

[9] AST注入 --

[10] The Secret Parameter, LFR, and Potential RCE in NodeJS Apps -- CAPTAINFREAK (2021)

[11] A tale of making internet pollution free -- s1r1us...

Lua

在漏洞挖掘中可能在这三种场景遇到lua:

1.单独出现:作为CGI或完全使用lua实现服务

2.嵌入与C模块:lua作为小巧且高效的语言经常和C混合使用,找luaopen_*它会在require时被自动调用,相关语法可见

3.openresty:作为2的特例,由于其性能很好因此正在被很多厂商采用成功焕发第二春因此需要多研究...

危险函数

  1. 代码执行:loadfiledofileloadstring()()requireloadlib
  2. 命令执行:executepopen
  3. 文件操作:open

附:还没遇到过,在有沙盒时可参考这里的函数

\0截断 文件系统/自己实现的代码

Perl

perl语言和其他的语法差别还是挺大的,可先看两个半小时学会Perl熟悉下。

危险函数

命令执行

1.system:和其他语言一样,当参数是字符串时用sh -c,当是数组时能安全处理它

2.exec/execvp:和Linux的exec一样不返回

3.` `:同php等语言

4.open:只有两个参数时,文件名的开头或结尾是|表示其他部分为命令并执行,三个参数时第二和第三个参数可控也同理

Golang

千言万语

Go是编译型语言,入口比较稳定,像web也可以直接从main开始跟路由/中间件注册情况...

项目布局

- my-go-project
 - cmd  入口代码如主函数放这里
 - pkg  /外部依赖
 - internal  内部库在编译时会检查约束其内部可以继续命名目录来指定给哪个应用使用
 - go.mod && go.sum  go模块依赖
 - Makefile  编译脚本

编译与调试

远程调试有两种方法,先编译或直接指定项目源码,它们都需要先安装dlv服务端:

# 直接使用delev编译并调试项目
dlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient

# 先编译带符号的项目再调试 
go build -gcflags "all=-N -l" github.com/app/demo  # 1.10及之后的版本
go build -gcflags "-N -l" github.com/app/demo  # 之前的版本
# 调试方式启动也可以使用attach方式附加在运行中的进程上
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./demo

其中为-gcflags编译选项,-N表示禁用优化,-l表示禁用内联。

常见组件

数据库

之前提到如果用了ORM框架一般都是比较安全,但是可能会漏掉一些或者某些点无法预编译,因此这里的检查就是查看所有的语句看是否存在问题...

Gorm

Exec, Raw, Select, Where

Sql

Query, QueryRow, QueryContext, QueryRowContext

Xorm

Query, QueryString, SQL, Where

Etcd

ETCD是一种用于分布式系统的键值型数据库,它主要用于存储配置信息,可使用etcdctl get --prefix ''获取所有的数据

WEB

Gin

序列化

protobuf

在调试接口时可以使用postman,它也支持websocket/grpc请求,不过离线环境不可用(垃圾!),所以替代的grpc可食用bloomrpc

通用问题

XSS

没问题的

如直接输出未进行任何过滤,使用模板...

特性问题

命令注入

exec.Command
exec.CommandContext -> CombinedOutput/Output/Run/Start

testres,err :=exec.Command("sh", "-c", testStr).Output()

**subshell**

文件写

os.OpenFile("./assets/img/"+handler.Filename,os.O_WRONLY|os.O_CREATE,0666) 

模板注入

它自带了text/templatehtml/template,前者容易出现xss后者会编码,除此之外的模板注入比较难利用,它的语法见文档

内存破坏

Go本身是内存安全的,但是为了提供一些烧操作接口,还是提供了两个库:unsafe可进行有限的指针运算,cgo能粘合C代码,因此当使用这两个库时需要仔细检查是否有内存破坏的问题...

未检查错误

当会返回错误状态时需要先检查错误再继续使用返回值,没检查可能会有问题...

整数溢出

Go的整数有长度限制,因此在进行运算或类型转换时可能存在溢出/截断...

自动化工具

  1. gosec: 通过分析Go代码的AST与规则匹配扫描问题(试了下各种报错...)
  2. gokart: 使用SSA做静态分析,支持污点追踪...

参考

  1. https://github.com/OWASP/Go-SCP

Misc

暂时不知道放哪里,就先扔这里吧,在挖掘CGI时为了调试方便可以先替换原文件并且抓包:

#!/bin/bash

echo 'Content-type: text/html'
echo 
# 输出环境变量
env
# 输出body
dd

或:

#!/usr/bin/python

import os
import sys

shellquote = lambda s: "'" + s.replace("'", r"'\''") + "'" # escape

res = ''
res += '\n'.join(["export %s=%s"%(i,shellquote(j)) for i,j in os.environ.items()]) # source <

with open('/tmp/post_data', 'wb') as f:
  f.write(sys.stdin.read())
print("Content-type: text/plain")
print("Content-Length: " + str(len(res)) + "\r\n")
with open('/tmp/cgienv', 'w') as f:
  f.write(res)
print(res)

之后再用直接source导入环境变量并使用<重定向输入即可,此时可用strace,或是gdb:

gdb file
> b *xxx
> run file params < /tmp/post_data

对于web服务器:

  1. 小众服务器优先研究它的目录遍历(IOT等设备)
  2. 在存在PHP-FPM时,可考虑通过它进行RCE (条件苛刻,需有socket型的SSRF)