Ubuntu下测试格式化字符串漏洞

基础内容可以参考

http://120.78.88.5/WooyunDrops/#!/drops/823.漏洞挖掘基础之格式化字符串

 

首先确保系统禁用了ASLR

执行以下命令禁用ASLR,防止地址动态分配造成实验失败

实验源码

gcc编译.关闭ld链接器不可执行机制,关闭gcc编译器gs验证码机制

gcc编译器gs验证码机制

gcc编译器专门为防止缓冲区溢出而采取的保护措施,具体方法是gcc首先在缓冲区被写入之前在buf的结束地址之后返回地址之前放入随机的gs验证码,并在缓冲区写入操作结束时检验该值。通常缓冲区溢出会从低地址到高地址覆写内存,所以如果要覆写返回地址,则需要覆写该gs验证码。这样就可以通过比较写入前和写入后gs验证码的数据,判断是否产生溢出。

关闭gcc编译器gs验证码机制的方法是:

在gcc编译时采用-fno-stack-protector选项。

ld链接器堆栈段不可执行机制

ld链接器在链接程序的时候,如果所有的.o文件的堆栈段都标记为不可执行,那么整个库的堆栈段才会被标记为不可执行;相反,即使只有一个.0文件的堆栈段被标记为可执行,那么整个库的堆栈段将被标记为可执行。检查堆栈段可执行性的方法是:

如果是检查ELF库:readelf -lW $BIN | grep GNU_STACK查看是否有E标记

如果是检查生成的.o文件:scanelf -e $BIN查看是否有X标记

ld链接器如果将堆栈段标记为不可执行,即使控制了eip产生了跳转,依然会产生段错误。

关闭ld链接器不可执行机制的方法是:

在gcc编译时采用-z execstack选项。

开始调试

首先分析一下汇编代码,下面这一段代码就是将p指向flag,并且将局部变量flag、p压栈,我们只需要利用格式化字符串漏洞覆盖掉*p指向的内存地址的内容为2000就可以了。

*p地址即位ebp-0x10 从上图我们可以看到为0xffffcfb8

所以,我们现在要将0xffffcfb8这个地址的内容修改为2000。

注: gdb调试环境里面的栈地址跟直接运行程序是不一样的,也就是说我们在直接运行程序时修改这个地址是没用的,所以我们需要结合格式化字符串漏洞读内存的功能,先泄露一个地址出来,然后我们根据泄露出来的地址计算出ebp-0x10的地址。

执行get()函数后随便输入AAAAAAA可以观察到栈区如下图

如果输入%x的话就可以读出esp+4地址上的数据 即0xffffcf54

所以说b8-54=64 计算出偏移量后我们可以去获取泄漏的地址然后再去覆盖

0xffffcfa4+0x64=0xffffd008

所以我们要修改的地址为0xffffd008

生成payload

成功

PreparedStatement防止sql注入

今天测试一个demo发现里面有点小问题,代码很规范,其中操作jdbc使用PreparedStatement也很正常,可是测试时候突然发现其采用了字符串追加形式来生产sql语句,这样使用其实直接绕开了PreparedStatement的优点[参数化的查询],从而可以轻易的造成sql注入。


首先我们先认识三个重要的对象

 1.Connection

代表着Java程序与数据库建立的连接。

2.Statement

代表SQL发送器,用于发送和执行SQL语句。

3.ResultSet

代表封装的数据库返回的结果集,用于获取查询结果。

 

上面是demo中的初始化方法和对象的使用,还有读取DbConfig.properties配置文件

什么是sql注入

简单来说就是黑客通过与服务器交互时在创建者没有有效的进行过滤的情况下的一种拼装sql命令来达到改变sql语义进而获取其敏感数据的行为.

 

那么如何进行有效的防御?我们还是在java中围绕这个demo说

它其中判断登陆用户是否成功的方法是这样写的

当我们尝试登陆一个错误用户,flag返回false,正确时返回true

可以看到,这时数据库执行的命令为

因为没有符合的返回,所以flag依然为false

可是当遇到别有用心的人时登陆就不会是这么简单的操作,比如我们执行下面的sql命令,数据库会回应怎样的数据呢?

可以看一下

返回了两个用户的信息,这也不用怎么解释,前面两个值与and返回为false而之后配一个or 1=1 也就相当于执行了

所以攻击者可以配合这样的语法来进行任意用户登陆,如 密码输入为

rs next了两次自然返回true

所以如何避免呢,在java中,Java提供了 Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询,PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程。

其中PreparedStatement就是可以有效防止常见的sql注入的一种方法

PreparedStatement是java.sql包下面的一个接口,用来执行SQL语句查询,通过调用 connection.preparedStatement(sql) 方法可以获得PreparedStatment对象。数据库系统会对sql语句进行预编译处理(如果JDBC驱动支持的话),预处理语句将被预先编译好,这条预编译的sql查询语句能在将来的查询中重用,这样一来,它比Statement对象生成的查询速度更快。

PreparedStatement与Statement区别

Statement是PreparedStatement的父类,作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。

Statement不对sql语句作处理而直接交给数据库;而PreparedStatement支持预编译,对于多次重复执行的sql语句,使用PreparedStament使代码的执行效率,代码的可读性和可维护性更高,PreparedStament提高了代码的安全性,防止sql注入。

安全性 效率 开销 可读性 维护性

prepareStatement

高,预编译 容易

Statement

容易发生sql注入 低,每一次编译 不容易

 

如何使用PreparedStatement

1:字符串追加形式的PreparedStatement

2:使用参数化查询的PreparedStatement


使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。

对于刚才的例子,可以看到它使用的是第一种方式这样其实就跟使用了Statement一样,这样的优点是代码少打几行可是代价也是很高的。如果我们改成第二种,PreparedStatement会对’进行转义,sql将其作为一个参数一个字段的属性值来处理,从而使得注入攻击失败

简单来说预编译会给你外面加引号并且过滤特殊字符

 

补充:避免SQL注入的第二种方式:
在组合SQL字符串的时候,先对所传入的参数做字符取代(将单引号字符取代为连续2个单引号字符,因为连续2个单引号字符在SQL数据库中会视为字符中的一个单引号字符。)

可以看出使用拼凑字符追加型的sql生成形式能更方便灵活的自定义控制sql语句,但如果没有进行有效的过滤很容易造成sql注入,所以建议使用参数化查询的PreparedStatement,用PreparedStatement代替Statement。

【注意】占位符只能占位SQL语句中的普通值,决不能占位表名、列名、SQL关键字(select、insert等)。所以如果使用动态表名,字段,就只能向上面案例那样使用非预编译方法,不过这样显然很容易导致注入。


参考:

http://www.importnew.com/5006.html

http://blog.csdn.net/changyinling520/article/details/71159652

http://blog.csdn.net/daijin888888/article/details/50965232

Jinja2 笔记

大部分摘自网上,方便平时查阅【更新ing…】

介绍

Jinja2:Jinja是日本寺庙的意思,并且寺庙的英文是temple和模板的英文template的发音类似。Jinja2是默认的仿Django模板的一个模板引擎,由Flask的作者开发。它速度快,被广泛使用,并且提供了可选的沙箱模板来保证执行环境的安全,它有以下优点:

  • 让前端开发者和后端开发者工作分离。
  • 减少Flask项目代码的耦合性,页面逻辑放在模板中,业务逻辑放在视图函数中,将页面逻辑和业务逻辑解耦有利于代码的维护。
  • 提供了控制语句、继承等高级功能,减少开发的复杂度。

模版

  • {{ ... }}:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。
  • {% ... %}:装载一个控制语句。
  • {# ... #}:装载一个注释,模板渲染的时候会忽视这中间的值。

变量

在模板中添加变量,可以使用(set)语句。

with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效

if语句

for语句

遍历字典

Jinja2中for循环内置常量

loop.index 当前迭代的索引(从1开始)
loop.index0 当前迭代的索引(从0开始)
loop.first 是否是第一次迭代,返回True\/False
loop.last 是否是最后一次迭代,返回True\/False
loop.length 序列的长度

 注意:不可以使用continuebreak表达式来控制循环的执行

阅读剩余部分 –

安恒杯一月赛部分题目writeup

首先感谢下安恒举办的比赛,还有大神们的讲解,学习了


再来个友情链接

发现.git泄漏,拿到源码

发现上传文件,代码审计

发现upload.php里面的条件竞争漏洞,先上传文件再删除文件。但是程序开头会检查权限,需要登录后才能操作。因此解题思路为结合CSRF+条件竞争。

因为程序添加友情链接时候会先去访问这个文件

所以我们可以写个自动上传文件的js,利用csrf去上传绕过user判断,然后利用时间差,在还没删除temp文件时快速去访问/temp上传成功的php,并自动创建一个一句话shell

poc:

阅读剩余部分 –

echarts+ajax+java制作图表

整理了下学校男女身高体重数据,顺便当复习java了

charts在绘图前我们需要为其准备一个具备高宽的 DOM 容器。

阅读剩余部分 –

jsp写入中文到mysql乱码问题

记录一下写作业遇到的问题

纠结了很久,代码是用servlet来处理form

其中post传输用户信息时一直乱码,最后查了半天才找见解决方法

 

  1. 在jsp页面的<%@ page 位置加上 contentType=”text/html; charset=utf-8″ %>,<meta 的位置也是如此:content=”text/html;charset=utf-8″ />
  2. 建立一个mysql数据库,字符集采用utf-8,建表也是如此。
  3. 在getConnection的URL中加上:?useUnicode=true&characterEncoding=UTF-8″,如:url = “jdbc:mysql://localhost:3306/(此处为数据库名称)?useUnicode=true&characterEncoding=UTF-8″,也就是在(数据库名称)的后面  //其中问号不能丢
  4. 如果你不是在jsp页面里直接处理form,也是和我一样采用servlet来处理form的话,那么在还要增加一个过滤器,这主要是因为采用的是tomcat的web发布器,如果用resin的话,不会有这样的问题。

    然后在WEB-INF目录下的web.xml文件,配置下过虑器

     

 

参考:http://blog.sina.com.cn/s/blog_6c5ad3d10100ligv.html

 

pwnable入门题笔记

fd

ssh连上

找到fd的源码

首先,程序接收一个参数argv[1],转换为整数型之后减0x1234 ,读入buf ,比较是否与LETMEWIN相同,如果相同则get flag

 

read函数

read()会把参数fd所指的文件传送nbyte个字节到buf指针所指的内存中。若参数nbyte为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或无可读取的数据。错误返回-1,并将根据不同的错误原因适当的设置错误码。

linux文件描述符

Integer value <stdio.h> file stream
0 stdin
1 stdout
2 stderr

也就是我们可以令fd=0 使得标准输入,buf的值就可以键入了

又因为

所以我们令fd=0x1234的10进制4660然后键入LETMEWIN

 

collision

先看代码

分析源码,首先输入长度为20的字符然后与关键函数check_password比较

p强制转化为指针,32位下指针一般与char的大小相同,所以相当于分成5组(char1个字节,int4个字节

然后把每四个字符看成一共int型的数字,进行5次循环相加,结果放入res中

最后符合hashcode = 0x21DD09EC; 拆分一下符合20长度限制,如下

注意下是小端模式

 

bof

给的源码:

key == 0xcafebabe 即可拿到shell但实际传的key为0xdeadbeef

又看到gets 可以溢出

IDA打开编译好的c

发现char s //[sp + 1Ch] [bp – 2Ch]说明,这个字符串s 是从[bp – 2Ch]处开始进入栈缓冲区的,所以我们只要覆盖2C字节,再加上EBP和EIP的8个字节,总共52个字节就可以成功覆盖第一个变量,也就是func里面的参数覆盖为0xcafebabe

这里盗个图方便理解

所以EXP为

拿到flag

大小端模式

大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

下面以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value

Big-Endian: 低地址存放高位,如下:

高地址
—————
buf[3] (0x78) — 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) — 高位
—————
低地址

Little-Endian: 低地址存放低位,如下:

高地址
—————
buf[3] (0x12) — 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) — 低位
————–
低地址
内存地址 小端模式存放内容 大端模式存放内容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

 


目前Intel的80×86系列芯片是唯一还在坚持使用小端的芯片,ARM芯片默认采用小端,但可以切换为大端;而MIPS等芯片要么采用全部大端的方式储存,要么提供选项支持大端——可以在大小端之间切换。另外,对于大小端的处理也和编译器的实现有关,在C语言中,默认是小端(但在一些对于单片机的实现中却是基于大端,比如Keil 51C),Java是平台无关的,默认是大端。在网络上传输数据普遍采用的都是大端。

百度杯ctf二月场几道web题 writeup

include

比较经典的文件包含题,记录一下

首先看phpinfo里allow_url_include为On

所以可以使用php://input包含执行命令

 

 

关键文件在dle345aae.php

配合

读取源码

爆破1

这里用到PHP超全局变量

$GLOBALS — 引用全局作用域中可用的全部变量

$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。

PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。

 

payload:

http://4687f7fdd5d64157a0dea153446a49728bbc8a7ce1bd49c6.game.ichunqiu.com/?hello=GLOBALS

 

爆破2

代码注入一下就ok

看来只有flag.php

然后我们直接输出源码就行

比较坑的是i春秋带了waf…我们改成post再传个值绕过

水过..

程序逻辑问题 writeup

最近准备刷一下实验吧的题,抽空做个小记录

题目连接:http://ctf5.shiyanbar.com/web/5/index.php


题目就是登陆成功用户后获得flag

首先获得php源码

其中

显然可以注入

接着往下看

这里把sql语句执行得到的数据放到row 并且判断其中的pw是否与输入的md5($_POST[pass]);相等

因为这里把用户和密码的判断分开 所以我们没法按常规注释掉密码的sql判断

但是因为row的数组结果是之前$sql的语句,所以我们其实可以通过sql注入生成一个密码来绕过

那就好办了,首先先试出来用户名为username

接下来构造sql语句

密码处填写123 成功拿到flag