Challenges

最后一章了!!!

Admin lost password

先用sqlmap探测一下,两个参数是否能sql注入,但似乎都不行。再试一下弱密码,也不行。

看看那张logo,下载了看看用Stegsolve看看。啥也看不出来,太难了,还是翻源码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
String PASSWORD = "!!webgoat_admin_1234!!";
@PostMapping("/challenge/1")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
boolean ipAddressKnown = true;
boolean passwordCorrect = "admin".equals(username) && PASSWORD.replace("1234", String.format("%04d",ImageServlet.PINCODE)).equals(password);
if (passwordCorrect && ipAddressKnown) {
return success(this).feedback("challenge.solved").feedbackArgs(Flag.FLAGS.get(1)).build();
} else if (passwordCorrect) {
return failed(this).feedback("ip.address.unknown").build();
}
return failed(this).build();
}

可以看出,答案是!!webgoat_admin_????!!,然后继续翻,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static final public int PINCODE = new SecureRandom().nextInt(10000);

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

byte[] in = new ClassPathResource("images/webgoat2.png").getInputStream().readAllBytes();

String pincode = String.format("%04d", PINCODE);

in[81216]=(byte) pincode.charAt(0);
in[81217]=(byte) pincode.charAt(1);
in[81218]=(byte) pincode.charAt(2);
in[81219]=(byte) pincode.charAt(3);

response.setContentType(MediaType.IMAGE_PNG_VALUE);
FileCopyUtils.copy(in, response.getOutputStream());
}

看出4个数字写在图片的81216-81216字节,81216的十六进制为13D40,用文件二进制查看器看一下。C1_hex

或者用python写下代码。

1
2
3
4
with open('logo.png','rb') as f:
f.seek(81216)
a=f.read(4)
print(str(a,'utf-8'))

那最后密码就是!!webgoat_admin_1046!!

Without password

同样还是先试试SQL注入,似乎用户名一定要是Larry,但是密码可以注入,输入'or true--就行了。

Admin password reset

同样,还是先发一封邮件给自己,看看url有什么规律,但是似乎是随机生成的,算了,还是翻源码吧。

这题原来是git泄露,访问http://127.0.0.1:8080/WebGoat/challenge/7/.git,把git包下载下了,解压,恢复至最新版,里面有6个文件。用反编译工具打开一个最显眼的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
import java.util.Random;

public class PasswordResetLink {
public String createPasswordReset(String paramString1, String paramString2) {
Random random = new Random();
if (paramString1.equalsIgnoreCase("admin"))
random.setSeed(paramString2.length());
return scramble(random, scramble(random, scramble(random, MD5.getHashString(paramString1))));
}

public static String scramble(Random paramRandom, String paramString) {
char[] arrayOfChar = paramString.toCharArray();
for (byte b = 0; b < arrayOfChar.length; b++) {
int i = paramRandom.nextInt(arrayOfChar.length);
char c = arrayOfChar[b];
arrayOfChar[b] = arrayOfChar[i];
arrayOfChar[i] = c;
}
return new String(arrayOfChar);
}

public static void main(String[] paramArrayOfString) {
if (paramArrayOfString == null || paramArrayOfString.length != 2) {
System.out.println("Need a username and key");
System.exit(1);
}
String str1 = paramArrayOfString[0];
String str2 = paramArrayOfString[1];
System.out.println("Generation password reset link for " + str1);
System.out.println("Created password reset link: " + (new PasswordResetLink()).createPasswordReset(str1, str2));
}
}

这里的Main函数都写好了,直接运行吧java PasswordResetLink admin xxx,但是还缺第二个参数,但是这里只要用到它的长度,可以一点一点试。但是试到10位数都不对。

又去翻源码了,源码中,这里的第二个参数是webgoat,但是,我生成的编号和它的就是不一样,可能是java的版本不同吧(我用的是java14),但源码中是硬编码写在里面的,只能直接用了。375afe1104f4a487a73823c50a9292a2

Without account

先随便点点,发现每次试图投票,控制台都会输出一堆数字。但也没什么用,继续翻源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping(value = "/challenge/8/vote/{stars}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<?> vote(@PathVariable(value = "stars") int nrOfStars, HttpServletRequest request) {
//Simple implementation of VERB Based Authentication
String msg = "";
if (request.getMethod().equals("GET")) {
var json = Map.of("error", true, "message", "Sorry but you need to login first in order to vote");
return ResponseEntity.status(200).body(json);
}
Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);
votes.put(nrOfStars, allVotesForStar + 1);
return ResponseEntity.ok().header("X-Flag", "Thanks for voting, your flag is: " + Flag.FLAGS.get(8)).build();
}

前面用的是GetMapping,但是后面又要求请求不是GET,所以随便写一个不常用的请求方式就行了,比如HEAD,flag就在返回消息的头部。这是一种叫做Authentication Bypass Using HTTP Verb Tampering的漏洞。

总结

用了一个国庆,把WebGoat全部做完了,收获还是很多的,了解了各种常见的Web漏洞,自然也知道了如何才能防范这些漏洞。以后有可能会继续做做DVWA,可以了解一下php世界的Web安全。