做任务游戏能赚钱的网站,建设厅网站合同备案在哪里,移动端什么意思,wordpress 文件地址原文链接#xff1a;https://cloud.tencent.com/developer/article/1534109一、代码审计相比黑盒渗透的漏洞挖掘方式#xff0c;代码审计具有更高的可靠性和针对性#xff0c;更多的是依靠对代码、架构的理解#xff1b;使用的审计工具一般选择Eclipse或IDEA#xff1b;审…原文链接https://cloud.tencent.com/developer/article/1534109一、代码审计相比黑盒渗透的漏洞挖掘方式代码审计具有更高的可靠性和针对性更多的是依靠对代码、架构的理解使用的审计工具一般选择Eclipse或IDEA审计工作过程主要有三步风险点发现——风险定位追踪——漏洞利用所以审计不出漏洞无非就是find“找不到该看哪些代码”和judge“定位到代码但判断不出有没有问题”。而风险点发现的重点则在于三个地方用户输入(入参)处检测绕过处漏洞触发处一般审计代码都是借助代码扫描工具(Fortify/Checkmarx)或从这三点着手。本系列选取WebGoat作为案例讲解漏洞的特征发现、定位技巧、调试及触发利用的具体过程尽量涵盖所有的挖掘场景最后补充实战挖掘案例。二、SQLi漏洞挖掘1、介绍SQLi是最著名也是影响最广的漏洞之一注入漏洞都是程序把用户输入的数据当做代码执行发现的关键有两个第一是用户能够控制输入第二是用户输入的数据被拼接到要执行的代码中从而被执行。2、挖掘过程这里以webgoat的数字型注入讲解SQLi漏洞的挖掘过程1) 定位特定功能模块的代码了解不同框架特性本系统的Springboot注解RequestMapping(path PATH)GetMapping(path PATH)PostMapping(path PATH)通过抓取请求数据包获取path特征SqlInjection/assignment5b使用IDEA的全局搜索功能(SHIFTCTRLF)定位到代码2) 代码分析SqlInjectionLesson5b.java类代码如下PostMapping(/SqlInjection/assignment5b)ResponseBodypublic AttackResult completed(RequestParam String userid, RequestParam String login_count, HttpServletRequest request) throws IOException {return injectableQuery(login_count, userid);}protected AttackResult injectableQuery(String login_count, String accountName) {String queryString SELECT * From user_data WHERE Login_Count ? and userid accountName;try {Connection connection DatabaseUtilities.getConnection(getWebSession());PreparedStatement query connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);这是一个典型的动态拼接用户输入和防范案例系统接收两个参数login_count和userid其中login_count通过“”直接拼接而userid首先通过类型转换为Integer赋值给count并经过了预编译(参数化请求)处理不存在SQLi漏洞。3)漏洞验证最后构造路径及参数POC验证漏洞存在3、漏洞分类挖掘技巧根据挖掘经验白盒挖掘层面大致可以将SQLi的类型分为六类1、入参直接动态拼接2、预编译有误3、框架注入(MybatisHibernate)4、order by 绕过预编译5、%和_绕过预编译6、SQLi检测绕过1) 参数直接拼接最明显的“”拼接思路一般有二通过关键字定位到SQL语句回溯参数是否是用户可控或通过跟踪用户输入是否执行SQL操作搜索的关键词有Select|insert|update|delete|java.sql.Connection|Statement|.execute|.executeQuery|jdbcTemplate|queryForInt|queryForObject|queryForMap|getConnection|PreparedStatement|Statement|execute|jdbcTemplate|queryForInt|queryForObject|queryForMap|executeQuery|getConnection2) 预编译有误并不是使用了预编译PreparedStatement一定就可以防止SQL注入动态拼接SQL同样存在SQLi注入这也是实际审计中高发的问题下面代码就是典型的预编译有误String query SELECT * FROM usersWHERE userid userid AND password password ;PreparedStatement stmt connection.prepareStatement(query);ResultSet rs stmt.executeQuery();定位预编译可以通过搜索关键函数setObject()、setInt()、setString()、setSQLXML()3) 框架注入Hibernate典型的注入代码为session.createQuery(from Book wheretitle like % userInput % and published true)或形如{StringBuffer queryString newStringBuffer();queryString.append(“from Test where id’”);queryString.append(id);queryString.append(‘\’’);}定位此框架的SQL注入首先需要在xml配置文件或import包里确认是否使用此框架然后使用关键字createQuerysession.save(session.update(session.delete进行定位。Mybatis有两种变量方法不安全的写法为select * from books where id ${id}安全的写法为JDBC预编译select * from books where id #{id}此外like、in和order by语句也需要使用#挖掘技巧则是在注解中或者Mybatis相关的配置文件中搜索 $。4) order by 绕过预编译类似下面sql语句 order by 后面是不能用预编译处理的只能通过拼接处理只能手动进行过滤详见案例。String sql “Select * from news where title ?” “order by‘” time “’asc”5) %和_绕过预编译预编译是不能处理%需要手动过滤否则会造成慢查询和DOS。6) SQLi检测绕过若SQL在处理过程中经过黑/白名单(正则)或Filter检测通常检测代码存在缺陷则可进行检测绕过。4、漏洞防御OWASP官方推荐的SQLi防御方案有四种1)预编译(参数化查询)PreparedStatement stmt connection.prepareStatement(SELECT * FROM users WHERE userid? ANDpassword?);stmt.setString(1, userid);stmt.setString(2, password);ResultSet rs stmt.executeQuery();2)存储过程使用CallableStatement对存储过程接口的实现来执行数据库查询SQL代码定义并存储在数据库本身中然后从应用程序中调用使用存储过程和预编译在防SQLi方面的效果是相同的。String custname request.getParameter(customerName);try {CallableStatement cs connection.prepareCall({callsp_getAccountBalance(?)});cs.setString(1, custname);ResultSet results cs.executeQuery();} catch (SQLException se) {}3)黑/白名单验证属于输入验证的范畴大多使用正则表达式限制或对于诸如排序顺序之类的简单操作最好将用户提供的输入转换为布尔值然后将该布尔值用于选择要附加到查询的安全值。public String someMethod(boolean sortOrder) {String SQLquery someSQL ... order by Salary (sortOrder ? ASC :DESC);4) 输出转义将用户输入放入查询之前对其进行转义OWASP企业安全性API(ESAPI)是一个免费的开源Web应用程序安全控制库。CodecORACLE_CODEC new OracleCodec();Stringquery SELECT user_id FROM user_data WHERE user_name ESAPI.encoder().encodeForSQL( ORACLE_CODEC,req.getParameter(userID)) and user_password ESAPI.encoder().encodeForSQL( ORACLE_CODEC,req.getParameter(pwd)) ;5)框架修复对于Mybatis框架select * from news where tile like concat(‘%’,#{title}, ‘%’)select * from news where id in#{item}Mybatis的order by语句可以选择在java层做映射或过滤用户输入进行防御。对于Hibernate(HQL)框架预编译方法一Query querysession.createQuery(“from Useruser where user.name:customername and user:customerage:age ”);query.setString(“customername”,name);query.setInteger(“customerage”,age);方法二String hql FROM User user where user.name? and user.age?;Query q session.createQuery(hql);q.setString(0, name);q.setInteger(1,age);5、实战案例1) Mybatis框架对文章删除功能进行审计articelId参数前端可控RequestMapping(/delete)public ModelAndView delete(HttpServletRequestrequest) {ModelAndView model newModelAndView();try {model.setViewName(this.getRequestUri(request));String[] aridArr request.getParameterValues(articelId);if (aridArr ! null aridArr.length 0) {this.deleteArticle(aridArr);}} catch (Exception e) {model.setViewName(this.setExceptionRequest(request,e));logger.error(AdminArticleController.delete()--error,e);}return model;}顺着变量的走向进行审计articelId赋值给aridArr而后进行了为空的判断不为空则执行deleteArticle操作跟踪定位此函数private void deleteArticle(String[]artidArr) {//删除数据中记录articleService.deleteArticleByIds(artidArr);EHCacheUtil.remove(CacheConstans.ARTICLE_GOOD_RECOMMEND);继续跟踪操作articleService类的deleteArticleByIds函数继而进入DAO层在ArticleDaoImpl.java内public void deleteArticleByIds(StringarticleIds) {this.delete(ArticleMapper.deleteArticleByIds,articleIds);}进入ArticleMapper.xml最终追踪到deleteArticleByIds的SQL语句使用了$拼接典型的Mybatis注入DELETEFROM EDU_ARTICLE WHERE EDU_ARTICLE.ARTICLE_ID IN (${value})除了顺向思维还可以通过逆向思维挖掘pom.xml中看到系统使用的是Mybatis框架可以直接去审查Maper.xml文件查看是否使用$拼接UTF-83.2.12.RELEASE3.2.71.7.31.72) Order by绕过预编译Webgoat一个order by的案例order by的参数orderExpression可以是一个selectExpression也可以是一个函数比如使用一个case语句。案例中可以根据IP或ID对数据进行排序对应代码为Server.javaGetMapping(produces MediaType.APPLICATION_JSON_VALUE)SneakyThrowsResponseBodypublic List sort(RequestParamString column) {Connection connection DatabaseUtilities.getConnection(webSession);PreparedStatement preparedStatement connection.prepareStatement(select id, hostname, ip, mac, status, descriptionfrom servers where status outof order order by column);ResultSet rs preparedStatement.executeQuery();List servers Lists.newArrayList();while (rs.next()) {Server server new Server(rs.getString(1), rs.getString(2),rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6));servers.add(server);}虽使用了预编译但仍拼接了order by参数column使用case探测语句探测(case when (true) then id else ip end)如果真则以id排序结果为3) 预编译有误查看FAQ页面数据功能getFaqPage函数前端获取page等参数publicFaqPageInfo getFaqPage(String tenantId, Map conditions,int page, int pageSize, String like) {try {tenantId WebUtil.getLoginTenantId();FaqPageInfo fpi FAQ_IO_SERVICE.getPageInfo(tenantId, conditions, page, pageSize, like);for (FaqModel fm : fpi.getData()) {fm.setWhitelistIds(WHITELIST_SERVICE.getWhiteListOnFaq(tenantId,fm.getId()));}return fpi;}跟踪FAQ_IO_SERVICE.getPageInfo调用FaqSqlAccess.queryByPage进行处理public FaqPageInfo getPageInfo(StringtenantId, Map conditions, int page, int pageSize, Stringlike)throws SQLException {FaqPageInfo pageInfo new FaqPageInfo();pageInfo.setData(FaqSqlAccess.queryByPage(tenantId, conditions, page,pageSize, like));pageInfo.setTotalSize(FaqSqlAccess.queryCount(tenantId, conditions,like));return pageInfo;}找到FaqSqlAccess.java里的queryByPage方法追踪到SQL语句public static ListqueryByPage(String tenantId, Map conditions, int page,int pageSize, String like)throws SQLException {List models new ArrayList();String language conditions.get(language);Connection connection null;PreparedStatement stmt null;try {connection MysqlUtils.getConnection();String sql select id, name, description, language, update_time,is_on from TOC_FAQ where tenant_id?;if (!CommonUtils.isEmptyStr(language))sql andlanguage language ;if (!CommonUtils.isEmptyStr(like))sql and name like% like %;sql order by update_time desc limit ?,?;stmt connection.prepareStatement(sql);stmt.setString(1, tenantId);stmt.setInt(2, (page - 1) * pageSize);stmt.setInt(3, pageSize);ResultSet resultSet stmt.executeQuery();发现此处使用了预编译但language和like参数实际是直接拼接存在SQL注入对于getFaqPage功能构造参数language:-if(substring(user(),1,1)0x01,sleep(5),0)-}进行验证。简单或复杂的SQL注入漏洞原理和审计方法相同只是对于业务繁杂的系统数据的走向和处理过程会比较复杂调用链跟踪难度会稍大一些需要更多耐心。一、代码审计相比黑盒渗透的漏洞挖掘方式代码审计具有更高的可靠性和针对性更多的是依靠对代码、架构的理解使用的审计工具一般选择Eclipse或IDEA审计工作过程主要有三步风险点发现——风险定位追踪——漏洞利用所以审计不出漏洞无非就是find“找不到该看哪些代码”和judge“定位到代码但判断不出有没有问题”。而风险点发现的重点则在于三个地方用户输入(入参)处检测绕过处漏洞触发处一般审计代码都是借助代码扫描工具(Fortify/Checkmarx)或从这三点着手。本系列选取WebGoat作为案例讲解漏洞的特征发现、定位技巧、调试及触发利用的具体过程尽量涵盖所有的挖掘场景最后补充实战挖掘案例。二、SQLi漏洞挖掘1、介绍SQLi是最著名也是影响最广的漏洞之一注入漏洞都是程序把用户输入的数据当做代码执行发现的关键有两个第一是用户能够控制输入第二是用户输入的数据被拼接到要执行的代码中从而被执行。2、挖掘过程这里以webgoat的数字型注入讲解SQLi漏洞的挖掘过程1) 定位特定功能模块的代码了解不同框架特性本系统的Springboot注解RequestMapping(path PATH)GetMapping(path PATH)PostMapping(path PATH)通过抓取请求数据包获取path特征SqlInjection/assignment5b使用IDEA的全局搜索功能(SHIFTCTRLF)定位到代码2) 代码分析SqlInjectionLesson5b.java类代码如下PostMapping(/SqlInjection/assignment5b)ResponseBodypublic AttackResult completed(RequestParam String userid, RequestParam String login_count, HttpServletRequest request) throws IOException {return injectableQuery(login_count, userid);}protected AttackResult injectableQuery(String login_count, String accountName) {String queryString SELECT * From user_data WHERE Login_Count ? and userid accountName;try {Connection connection DatabaseUtilities.getConnection(getWebSession());PreparedStatement query connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);这是一个典型的动态拼接用户输入和防范案例系统接收两个参数login_count和userid其中login_count通过“”直接拼接而userid首先通过类型转换为Integer赋值给count并经过了预编译(参数化请求)处理不存在SQLi漏洞。3)漏洞验证最后构造路径及参数POC验证漏洞存在3、漏洞分类挖掘技巧根据挖掘经验白盒挖掘层面大致可以将SQLi的类型分为六类1、入参直接动态拼接2、预编译有误3、框架注入(MybatisHibernate)4、order by 绕过预编译5、%和_绕过预编译6、SQLi检测绕过1) 参数直接拼接最明显的“”拼接思路一般有二通过关键字定位到SQL语句回溯参数是否是用户可控或通过跟踪用户输入是否执行SQL操作搜索的关键词有Select|insert|update|delete|java.sql.Connection|Statement|.execute|.executeQuery|jdbcTemplate|queryForInt|queryForObject|queryForMap|getConnection|PreparedStatement|Statement|execute|jdbcTemplate|queryForInt|queryForObject|queryForMap|executeQuery|getConnection2) 预编译有误并不是使用了预编译PreparedStatement一定就可以防止SQL注入动态拼接SQL同样存在SQLi注入这也是实际审计中高发的问题下面代码就是典型的预编译有误String query SELECT * FROM usersWHERE userid userid AND password password ;PreparedStatement stmt connection.prepareStatement(query);ResultSet rs stmt.executeQuery();定位预编译可以通过搜索关键函数setObject()、setInt()、setString()、setSQLXML()3) 框架注入Hibernate典型的注入代码为session.createQuery(from Book wheretitle like % userInput % and published true)或形如{StringBuffer queryString newStringBuffer();queryString.append(“from Test where id’”);queryString.append(id);queryString.append(‘\’’);}定位此框架的SQL注入首先需要在xml配置文件或import包里确认是否使用此框架然后使用关键字createQuerysession.save(session.update(session.delete进行定位。Mybatis有两种变量方法不安全的写法为select * from books where id ${id}安全的写法为JDBC预编译select * from books where id #{id}此外like、in和order by语句也需要使用#挖掘技巧则是在注解中或者Mybatis相关的配置文件中搜索 $。4) order by 绕过预编译类似下面sql语句 order by 后面是不能用预编译处理的只能通过拼接处理只能手动进行过滤详见案例。String sql “Select * from news where title ?” “order by‘” time “’asc”5) %和_绕过预编译预编译是不能处理%需要手动过滤否则会造成慢查询和DOS。6) SQLi检测绕过若SQL在处理过程中经过黑/白名单(正则)或Filter检测通常检测代码存在缺陷则可进行检测绕过。4、漏洞防御OWASP官方推荐的SQLi防御方案有四种1)预编译(参数化查询)PreparedStatement stmt connection.prepareStatement(SELECT * FROM users WHERE userid? ANDpassword?);stmt.setString(1, userid);stmt.setString(2, password);ResultSet rs stmt.executeQuery();2)存储过程使用CallableStatement对存储过程接口的实现来执行数据库查询SQL代码定义并存储在数据库本身中然后从应用程序中调用使用存储过程和预编译在防SQLi方面的效果是相同的。String custname request.getParameter(customerName);try {CallableStatement cs connection.prepareCall({callsp_getAccountBalance(?)});cs.setString(1, custname);ResultSet results cs.executeQuery();} catch (SQLException se) {}3)黑/白名单验证属于输入验证的范畴大多使用正则表达式限制或对于诸如排序顺序之类的简单操作最好将用户提供的输入转换为布尔值然后将该布尔值用于选择要附加到查询的安全值。public String someMethod(boolean sortOrder) {String SQLquery someSQL ... order by Salary (sortOrder ? ASC :DESC);4) 输出转义将用户输入放入查询之前对其进行转义OWASP企业安全性API(ESAPI)是一个免费的开源Web应用程序安全控制库。CodecORACLE_CODEC new OracleCodec();Stringquery SELECT user_id FROM user_data WHERE user_name ESAPI.encoder().encodeForSQL( ORACLE_CODEC,req.getParameter(userID)) and user_password ESAPI.encoder().encodeForSQL( ORACLE_CODEC,req.getParameter(pwd)) ;5)框架修复对于Mybatis框架select * from news where tile like concat(‘%’,#{title}, ‘%’)select * from news where id in#{item}Mybatis的order by语句可以选择在java层做映射或过滤用户输入进行防御。对于Hibernate(HQL)框架预编译方法一Query querysession.createQuery(“from Useruser where user.name:customername and user:customerage:age ”);query.setString(“customername”,name);query.setInteger(“customerage”,age);方法二String hql FROM User user where user.name? and user.age?;Query q session.createQuery(hql);q.setString(0, name);q.setInteger(1,age);5、实战案例1) Mybatis框架对文章删除功能进行审计articelId参数前端可控RequestMapping(/delete)public ModelAndView delete(HttpServletRequestrequest) {ModelAndView model newModelAndView();try {model.setViewName(this.getRequestUri(request));String[] aridArr request.getParameterValues(articelId);if (aridArr ! null aridArr.length 0) {this.deleteArticle(aridArr);}} catch (Exception e) {model.setViewName(this.setExceptionRequest(request,e));logger.error(AdminArticleController.delete()--error,e);}return model;}顺着变量的走向进行审计articelId赋值给aridArr而后进行了为空的判断不为空则执行deleteArticle操作跟踪定位此函数private void deleteArticle(String[]artidArr) {//删除数据中记录articleService.deleteArticleByIds(artidArr);EHCacheUtil.remove(CacheConstans.ARTICLE_GOOD_RECOMMEND);继续跟踪操作articleService类的deleteArticleByIds函数继而进入DAO层在ArticleDaoImpl.java内public void deleteArticleByIds(StringarticleIds) {this.delete(ArticleMapper.deleteArticleByIds,articleIds);}进入ArticleMapper.xml最终追踪到deleteArticleByIds的SQL语句使用了$拼接典型的Mybatis注入DELETEFROM EDU_ARTICLE WHERE EDU_ARTICLE.ARTICLE_ID IN (${value})除了顺向思维还可以通过逆向思维挖掘pom.xml中看到系统使用的是Mybatis框架可以直接去审查Maper.xml文件查看是否使用$拼接UTF-83.2.12.RELEASE3.2.71.7.31.72) Order by绕过预编译Webgoat一个order by的案例order by的参数orderExpression可以是一个selectExpression也可以是一个函数比如使用一个case语句。案例中可以根据IP或ID对数据进行排序对应代码为Server.javaGetMapping(produces MediaType.APPLICATION_JSON_VALUE)SneakyThrowsResponseBodypublic List sort(RequestParamString column) {Connection connection DatabaseUtilities.getConnection(webSession);PreparedStatement preparedStatement connection.prepareStatement(select id, hostname, ip, mac, status, descriptionfrom servers where status outof order order by column);ResultSet rs preparedStatement.executeQuery();List servers Lists.newArrayList();while (rs.next()) {Server server new Server(rs.getString(1), rs.getString(2),rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6));servers.add(server);}虽使用了预编译但仍拼接了order by参数column使用case探测语句探测(case when (true) then id else ip end)如果真则以id排序结果为3) 预编译有误查看FAQ页面数据功能getFaqPage函数前端获取page等参数publicFaqPageInfo getFaqPage(String tenantId, Map conditions,int page, int pageSize, String like) {try {tenantId WebUtil.getLoginTenantId();FaqPageInfo fpi FAQ_IO_SERVICE.getPageInfo(tenantId, conditions, page, pageSize, like);for (FaqModel fm : fpi.getData()) {fm.setWhitelistIds(WHITELIST_SERVICE.getWhiteListOnFaq(tenantId,fm.getId()));}return fpi;}跟踪FAQ_IO_SERVICE.getPageInfo调用FaqSqlAccess.queryByPage进行处理public FaqPageInfo getPageInfo(StringtenantId, Map conditions, int page, int pageSize, Stringlike)throws SQLException {FaqPageInfo pageInfo new FaqPageInfo();pageInfo.setData(FaqSqlAccess.queryByPage(tenantId, conditions, page,pageSize, like));pageInfo.setTotalSize(FaqSqlAccess.queryCount(tenantId, conditions,like));return pageInfo;}找到FaqSqlAccess.java里的queryByPage方法追踪到SQL语句public static ListqueryByPage(String tenantId, Map conditions, int page,int pageSize, String like)throws SQLException {List models new ArrayList();String language conditions.get(language);Connection connection null;PreparedStatement stmt null;try {connection MysqlUtils.getConnection();String sql select id, name, description, language, update_time,is_on from TOC_FAQ where tenant_id?;if (!CommonUtils.isEmptyStr(language))sql andlanguage language ;if (!CommonUtils.isEmptyStr(like))sql and name like% like %;sql order by update_time desc limit ?,?;stmt connection.prepareStatement(sql);stmt.setString(1, tenantId);stmt.setInt(2, (page - 1) * pageSize);stmt.setInt(3, pageSize);ResultSet resultSet stmt.executeQuery();发现此处使用了预编译但language和like参数实际是直接拼接存在SQL注入对于getFaqPage功能构造参数language:-if(substring(user(),1,1)0x01,sleep(5),0)-}进行验证。简单或复杂的SQL注入漏洞原理和审计方法相同只是对于业务繁杂的系统数据的走向和处理过程会比较复杂调用链跟踪难度会稍大一些需要更多耐心。