Voici un service qui permet simplement de vérifier si le flag est correct.

We have a service that allows us to check if a flag is correct.

Index

Exploit

We find the following script on the page:

function checkFlag() {
    check = Module.cwrap("check", "number", ["string"]), flag = $("#flag").val(), check(flag) ? ($("#feedback").html('<div id="alert" class="alert alert-dismissible alert-success"><button type="button" class="close" data-dismiss="alert">&times;</button><strong>Congratulations!</strong> You can enter this flag in the CTFd.</div>'), $("#feedback").show()) : ($("#feedback").html('<div id="alert" class="alert alert-dismissible alert-danger"><button type="button" class="close" data-dismiss="alert">&times;</button><strong>Incorrect!</strong> Please check your flag again.</div>'), $("#feedback").show())
}
$("#btn-clear").on("click", (function(e) {
    e.preventDefault(), $("#flag").val(""), $("#feedback").hide()
})), $("#btn-check").on("click", (function(e) {
    checkFlag()
})), $(document).keyup((function(e) {
    "Escape" === e.key ? $("#feedback").html("") : "Enter" === e.key && checkFlag()
}))

We also find an include to another javascript file called index.js (I didn’t paste it here ‘cause it’s quite long).

The script on the page uses a module that is declared in index.js.

This file contains the declaration of the module, an interesting thing is that the module uses WebAssembly (WASM).

All our functions and data are declared in the index.wasm file:

(module
  (type $type0 (func (param i32) (result i32)))
  (type $type1 (func))
  (type $type2 (func (param i32)))
  (type $type3 (func (result i32)))
  (import "a" "memory" (memory $memory0 256 256))
  (global $global0 (mut i32) (i32.const 5244480))
  (export "a" (func $func5))
  (export "b" (func $func4))
  (export "c" (func $func2))
  (export "d" (func $func1))
  (export "e" (func $func0))
  (func $func0 (param $var0 i32)
    get_local $var0
    set_global $global0
  )
  (func $func1 (param $var0 i32) (result i32)
    get_global $global0
    get_local $var0
    i32.sub
    i32.const -16
    i32.and
    tee_local $var0
    set_global $global0
    get_local $var0
  )
  (func $func2 (result i32)
    get_global $global0
  )
  (func $func3 (param $var0 i32) (result i32)
    (local $var1 i32) (local $var2 i32) (local $var3 i32) (local $var4 i32) (local $var5 i32)
    i32.const 70
    set_local $var3
    i32.const 1024
    set_local $var1
    block $label0
      get_local $var0
      i32.load8_u
      tee_local $var2
      i32.eqz
      br_if $label0
      loop $label2
        block $label1
          get_local $var2
          get_local $var1
          i32.load8_u
          tee_local $var4
          i32.ne
          br_if $label1
          get_local $var3
          i32.const -1
          i32.add
          tee_local $var3
          i32.eqz
          br_if $label1
          get_local $var4
          i32.eqz
          br_if $label1
          get_local $var1
          i32.const 1
          i32.add
          set_local $var1
          get_local $var0
          i32.load8_u offset=1
          set_local $var2
          get_local $var0
          i32.const 1
          i32.add
          set_local $var0
          get_local $var2
          br_if $label2
          br $label0
        end $label1
      end $label2
      get_local $var2
      set_local $var5
    end $label0
    get_local $var5
    i32.const 255
    i32.and
    get_local $var1
    i32.load8_u
    i32.sub
  )
  (func $func4 (param $var0 i32) (result i32)
    (local $var1 i32) (local $var2 i32)
    get_local $var0
    i32.load8_u
    tee_local $var2
    if
      get_local $var0
      set_local $var1
      loop $label0
        get_local $var1
        get_local $var2
        i32.const 3
        i32.xor
        i32.store8
        get_local $var1
        i32.load8_u offset=1
        set_local $var2
        get_local $var1
        i32.const 1
        i32.add
        set_local $var1
        get_local $var2
        br_if $label0
      end $label0
    end
    get_local $var0
    call $func3
    i32.eqz
  )
  (func $func5
    nop
  )
  (data (i32.const 1024)
    "E@P@x4f1g7f6ab:42`1g:f:7763133;e0e;03`6661`bee0:33fg732;b6fea44be34g0~"
  )
)

We have an interesting data at the end that might be the flag.

In index.html we saw that it uses the function checkFlag and that function uses the check attributes in the module.

In index.js we see that this check attributes is related to the b function in the WASM.

var _check = Module["_check"] = function() {
    return (_check = Module["_check"] = Module["asm"]["b"]).apply(null, arguments)
};

The b function in the WASM is the fourth function:

(export "b" (func $func4))

So we have to understand what the following code does:

(func $func4 (param $var0 i32) (result i32)
	(local $var1 i32) (local $var2 i32)
	get_local $var0
	i32.load8_u
	tee_local $var2
	if
		get_local $var0
		set_local $var1
		loop $label0
			get_local $var1
			get_local $var2
			i32.const 3
			i32.xor
			i32.store8
			get_local $var1
			i32.load8_u offset=1
			set_local $var2
			get_local $var1
			i32.const 1
			i32.add
			set_local $var1
			get_local $var2
			br_if $label0
		end $label0
	end
	get_local $var0
	call $func3
	i32.eqz
)

It’s pretty simple, this function loop through all the chars in a string that we pass as parameter, and for each character it xor it with 3.

So all we have to do, is apply the same algorithm on what might be the key (the data part).

Here’s the python script:

flag = "E@P@x4f1g7f6ab:42`1g:f:7763133;e0e;03`6661`bee0:33fg732;b6fea44be34g0~"
key = ""
for value in flag:
    key += chr(ord(value) ^ 3)

print(key)

We’ve found the flag! FCSC{7e2d4e5ba971c2d9e944502008f3f830c5552caff3900ed4018a5efb77af07d3}

This was a nice challenge, I didn’t know much about WebAssembly so it was a good introduction to it.

Thanks to the FCSC team, this was an awesome CTF, I’ve learned a lot and had a lot of fun. Congratulation for the organization, see you next year!