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
,用文件二进制查看器看一下。
或者用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) { 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安全。