WebGoat之路-8-Challenges

2020-10-08
#ctf #WebGoat

Challenges

最后一章了!!!

Admin lost password

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

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

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_????!!,然后继续翻,

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写下代码。

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,代码如下:

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

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

@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安全。