plaidctf2019
Triggered(web)
首页的一段话提示了一点,要试图登录admin角色,页面功能分为登录,注册,先注册随意账号并登陆:
有查询和添加主题两个功能,尝试了一下,new note没法xss,所以可能就是查询了,以admin的身份进行flag查询
分析代码,服了,存sql写的,pl/pgsql(以postgresql支持)
关注登录过程:
用户:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60---------- POST /login
CREATE FUNCTION web.handle_post_login() RETURNS TRIGGER AS $$
DECLARE
form_username text;
session_uid uuid;
form_user_uid uuid;
context jsonb;
BEGIN
SELECT
web.get_form(NEW.uid, 'username')
INTO form_username;
SELECT
web.get_cookie(NEW.uid, 'session')::uuid
INTO session_uid;
SELECT
uid
FROM
web.user
WHERE
username = form_username
INTO form_user_uid;
IF form_user_uid IS NOT NULL
THEN
INSERT INTO web.session (
uid,
user_uid,
logged_in
) VALUES (
COALESCE(session_uid, uuid_generate_v4()),
form_user_uid,
FALSE
)
ON CONFLICT (uid)
DO UPDATE
SET
user_uid = form_user_uid,
logged_in = FALSE
RETURNING uid
INTO session_uid;
PERFORM web.set_cookie(NEW.uid, 'session', session_uid::text);
PERFORM web.respond_with_redirect(NEW.uid, '/login/password');
ELSE
PERFORM web.respond_with_redirect(NEW.uid, '/login');
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER route_post_login
BEFORE INSERT
ON web.request
FOR EACH ROW
WHEN (NEW.path = '/login' AND NEW.method = 'POST')
EXECUTE PROCEDURE web.handle_post_login();
密码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64---------- POST /login/password
CREATE FUNCTION web.handle_post_login_password() RETURNS TRIGGER AS $$
DECLARE
form_password text;
session_uid uuid;
success boolean;
BEGIN
SELECT
web.get_cookie(NEW.uid, 'session')::uuid
INTO session_uid;
IF session_uid IS NULL
THEN
PERFORM web.respond_with_redirect(NEW.uid, '/login');
RETURN NEW;
END IF;
SELECT
web.get_form(NEW.uid, 'password')
INTO form_password;
IF form_password IS NULL
THEN
PERFORM web.respond_with_redirect(NEW.uid, '/login/password');
RETURN NEW;
END IF;
SELECT EXISTS (
SELECT
*
FROM
web.user usr
INNER JOIN web.session session
ON usr.uid = session.user_uid
WHERE
session.uid = session_uid
AND usr.password_hash = crypt(form_password, usr.password_hash)
)
INTO success;
IF success
THEN
UPDATE web.session
SET
logged_in = TRUE
WHERE
uid = session_uid;
PERFORM web.respond_with_redirect(NEW.uid, '/');
ELSE
PERFORM web.respond_with_redirect(NEW.uid, '/login/password');
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER route_post_login_password
BEFORE INSERT
ON web.request
FOR EACH ROW
WHEN (NEW.path = '/login/password' AND NEW.method = 'POST')
EXECUTE PROCEDURE web.handle_post_login_password();
登录过程:
用户名登入:建立uid,session,将loggin设置为false
密码登入:判断session,将session设为true
总过程:
1.获取用户提交的用户名和存储在cookie表中的 session_uid
2.根据用户名,从 user表中查询出来 form_user_uid
3.然后将 session_uid 和 form_user_uid 和为False的登录状态写入到 session表中,如果session_uid为空(就是用户请求的时候不带session),则为此用户重新生成一个。 如果 session_uid 在数据库中已经存在,就4.修改这个 session_uid 对应的 user_uid 为当前登录的用户的id,登录状态设置为false 。
5.接下来设置 cookie , 并跳转到 /login/password
6.接下来是 post 到 /login/password 的处理流程,同样是获取 session_uid和用户输入的password , 然后把 user表和session表以user_uid相等为条件做一个连接,以 session_uid 和 password 为条件做一次查询。
如果查询到,就更新用户的session为登录状态
思路就是:
1.非admin用户登陆后,在密码登入的同时给一个线程admin登录,这时admin就可以成功登入
1 | import aiohttp |
2.利用query查询间隙的时间窗口登入admin
1 | #!/usr/bin/python |
reference:
https://cr0wn.uk/2019/plaid-triggered/
https://blog.wonderkun.cc/2019/04/15/plaidCTF%E4%B8%A4%E9%81%93web%E9%A2%98%E7%9B%AEwriteup/#more
Author: damn1t
Link: http://microvorld.com/2019/04/21/CTF/plaidctf2019/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.