After opening and exploring `server.go` script, you can find line 141 in main func:`	http.HandleFunc("/login", loginHandler)`.
Now we understand, that url with `/login` at the end calls `loginHandler` function.
Let's look inside:
```func loginHandler(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {		fmt.Fprintf(w, "500: %v", err)		return	}	username := r.FormValue("username")	password := r.FormValue("password")
	if strings.ToLower(username) != "admin" {		fmt.Fprintf(w, "User not found!\n")		return	}
	if MD5([]byte(password)) == "90829146b3603e2e7daf5031b2103e9e" {		fmt.Fprintf(w, "Login successful! Flag is %s\n", flag)	} else {		fmt.Fprintf(w, "Password is not correct!\nExpected %s, got %s\n", "90829146b3603e2e7daf5031b2103e9e", MD5([]byte(password)))	}}```So, there is 2 values which server checks: `username` and `password`.
It means, that correct url-request looks like this: `http://tasks.kksctf.ru:30020/login?username=admin&password=qwerty`
(Of course, we want to log in as admin, and `admin` is common username for it).***With `qwerty` password, server returns this:```Password is not correct!Expected 90829146b3603e2e7daf5031b2103e9e, got 67e129628458ce06fbc5bb76a58c5ca4```Because, as we have seen earlier, `MD5()` func result compares with `90829146b3603e2e7daf5031b2103e9e`.
To understand, how we can hack it, let's dive into this function:```func MD5(data []byte) string {	b := digest(data) 	return hex.EncodeToString(b[:])}```and further into `digest(data)`:```func digest(data []byte) [16]byte {	digest := md5.Sum(data) 	s := len(data) / 4	if s > 4 {		s = 4	} 	for ss := 0; ss < s; ss++ {		var F uint32
		F = ^F
		for i := 0; i < 4; i++ {			F = (F << 8) ^ table[(F>>24)^(uint32(data[ss*4+i])&0xff)]		}		F = ^F
		digest[ss*4+3] = byte((F >> 24) & 0xff)		digest[ss*4+2] = byte((F >> 16) & 0xff)		digest[ss*4+1] = byte((F >> 8) & 0xff)		digest[ss*4+0] = byte(F & 0xff)	} 	return digest}```Let's look at:```s := len(data) / 4if s > 4 {    s = 4}```What is `s`? It represents, how many groups of 4 bytes are in our password
(If password length > 16, than 17, 18 and other characters simply ignores).
Further we iterate not through every byte, but every group of 4 bytes:```for ss := 0; ss < s; ss++ {    ...}```It's important. Algorithm processes the password in groups of 4 bytes **separately**.
Notice, that if password is less than 4 characters, program never gets into this loop, and the whole function return just password's md5 hash.
At the end of this loop there are such lines:```digest[ss*4+3] = byte((F >> 24) & 0xff)digest[ss*4+2] = byte((F >> 16) & 0xff)digest[ss*4+1] = byte((F >> 8) & 0xff)digest[ss*4+0] = byte(F & 0xff)```We can see, that 4 bytes of `F` variable (`F` is `uint32`, so it takes only 4 bytes) writes to the current group of 4 bytes.
First useful fact, is that bytes are written in reverse order (little-endian).
Second — is that every byte is rewritten, so nothing remains of md5 and we don't need to reverse it!
Final hash - is simply some 4 numbers (`F`).
Now we can try to reverse this function deeper to find characters from `F`, but let's notice, that we can simply bruteforce every such group of 4 characters!
Byte is 256 possible values, so 4 bytes is 256⁴ = 4294967296, which is not too much.***But we need to gets this F's to compare with. Here is our admin's password hash: `90829146b3603e2e7daf5031b2103e9e`.
Firstly, split it into 4 bytes groups: `90829146 b3603e2e 7daf5031 b2103e9e`.
Now remember, that it is little-endian, so reverse bytes for every number: `46918290 2e3e60b3 3150af7d 9e3e10b2`.
Now we have F's to compare with!
So, here is dirty bruteforce code (from `F = ^F` to `F = ^F` is original algorithm code):```	for a := byte(0); a < 255; a++ {		for b := byte(0); b < 255; b++ {			for c := byte(0); c < 255; c++ {				for d := byte(0); d < 255; d++ {					var F uint32					F = ^F					for i := 0; i < 4; i++ {						if i == 0 {							F = (F << 8) ^ table[(F>>24)^(uint32(a)&0xff)]						}						if i == 1 {							F = (F << 8) ^ table[(F>>24)^(uint32(b)&0xff)]						}						if i == 2 {							F = (F << 8) ^ table[(F>>24)^(uint32(c)&0xff)]						}						if i == 3 {							F = (F << 8) ^ table[(F>>24)^(uint32(d)&0xff)]						}					}					F = ^F 					if F == 0x46918290 {						fmt.Println("=== 1 ===")						fmt.Println(a)						fmt.Println(b)						fmt.Println(c)						fmt.Println(d)					}					if F == 0x2e3e60b3 {						fmt.Println("=== 2 ===")						fmt.Println(a)						fmt.Println(b)						fmt.Println(c)						fmt.Println(d)					}					if F == 0x3150af7d {						fmt.Println("=== 3 ===")						fmt.Println(a)						fmt.Println(b)						fmt.Println(c)						fmt.Println(d)					}					if F == 0x9e3e10b2 {						fmt.Println("=== 4 ===")						fmt.Println(a)						fmt.Println(b)						fmt.Println(c)						fmt.Println(d)					}				}			}		}	}```After about 20 seconds we get this:```=== 1 ===41824199=== 4 ===71747545=== 3 ===756265119=== 2 ===1075211494```Turn it to right order: `41 82 41 99 107 52 114 94 75 62 65 119 71 74 75 45`
And then to ASCII: `)R)ck4r^K>AwGJK-`***Input it as password: `http://tasks.kksctf.ru:30020/login?username=admin&password=)R)ck4r^K%3EAwGJK-` and server returns...```Login successful! Flag is kks{1f_s0meth1ng_called_md5_1t_d0esnt_have_t0_be}```Nice, we found the flag without deep reversing! :D