Rating:
You can read the full writeup [here](https://lim1ts.github.io/ctf/2017/10/19/hackluTriangles.html), which is more wordy.
More succintly, observe that there are some .js files in the source.
In secret.js,
```
function test_pw(e, _) {
var t = stoh(atob(getBase64Image("eye")))
, r = 4096
, m = 8192
, R = 12288
, a = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
a.reg_write_i32(uc.ARM_REG_R9, m),
a.reg_write_i32(uc.ARM_REG_R10, R),
a.reg_write_i32(uc.ARM_REG_R8, _.length),
a.mem_map(r, 4096, uc.PROT_ALL);
for (var o = 0; o < o1.length; o++)
a.mem_write(r + o, [t[o1[o]]]);
a.mem_map(m, 4096, uc.PROT_ALL),
a.mem_write(m, stoh(_)),
a.mem_map(R, 4096, uc.PROT_ALL),
a.mem_write(R, stoh(e));
var u = r
, c = r + o1.length;
return a.emu_start(u, c, 0, 0),
a.reg_read_i32(uc.ARM_REG_R5)
}
function enc_pw(e) {
var _ = stoh(atob(getBase64Image("frei")))
, t = 4096
, r = 8192
, m = 12288
, R = new uc.Unicorn(uc.ARCH_ARM,uc.MODE_ARM);
R.reg_write_i32(uc.ARM_REG_R8, r),
R.reg_write_i32(uc.ARM_REG_R9, m),
R.reg_write_i32(uc.ARM_REG_R10, e.length),
R.mem_map(t, 4096, uc.PROT_ALL);
for (var a = 0; a < o2.length; a++)
R.mem_write(t + a, [_[o2[a]]]);
R.mem_map(r, 4096, uc.PROT_ALL),
R.mem_write(r, stoh(e)),
R.mem_map(m, 4096, uc.PROT_ALL);
var o = t
, u = t + o2.length;
return R.emu_start(o, u, 0, 0),
htos(R.mem_read(m, e.length))
}
function get_pw() {
for (var e = stoh(atob(getBase64Image("templar"))), _ = "", t = 0; t < o3.length; t++)
_ += String.fromCharCode(e[o3[t]]);
return _
}
```
UnicornJS is being used here, so we know that we need to extract the ARM code being used.
To do this,
```
function getARM1(){
var x = stoh(atob(getBase64Image("frei")));
var output = new Array();
for(var i = 0; i < o2.length ; i++){
output[i] = x[o2[i]];
}
return output;
}
//Looking at o2, we observe that our output will be in integers.
//Lets try converting them to hex values.
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}
```
By doing `toHexString(getARM1())`, we get this "0800a0e10910a0e10a20a0e10030a0e30050a0e30040d0e5010055e30100001a036003e2064084e0064084e2015004e20040c1e5010080e2011081e2013083e2020053e1f2ffffba0000a0e30010a0e30020a0e30030a0e30040a0e30050a0e30060a0e30070a0e30090a0e300a0a0e3"
We then paste this hex-string into an [online converter](http://armconverter.com/hextoarm/) to receive:
```
; FROM test_pw:
MOV R0, SB ; R0 = SB, Static base register. This is a synonym for R9. #R0 = R9 = m. (the secret password is here)
MOV R1, SL ; R1 = SL, Stack Limit register. This is a synonym for R10. #R1 = R10 = R (the input password is here)
MOV R3, R8 ; R8 = input password length
MOV R4, #0
MOV R5, #0
MOV IP, #0
LDRB R2, [R0] ; Load secret password
LDRB R6, [R1] ; Load input password
ADD R6, R6, #5 ; Will do +5
AND IP, R4, #1 ; ip == R4 and 1
CMP IP, #0
BEQ #0x34 ; Will jump when IP is 0, or rather when R4 is even
SUB R6, R6, #3 ; Will do -3 this when R4 is odd.
CMP R2, R6 ; 0x34 here, if R4 is even, just compare.
BNE #0x54 ; R2 needs == R6
ADD R0, R0, #1
ADD R1, R1, #1
ADD R4, R4, #1 ; Increment all. R4 is a counter
CMP R4, R3 ; Check if counter < input length
BLT #0x18
MOV R5, #1 ; We need this!
MOV R0, #0 ; 0x54 here
MOV R1, #0
MOV R2, #0
MOV R3, #0
MOV R4, #0
MOV R6, #0
MOV R7, #0
MOV R8, #0
MOV SB, #0
MOV SL, #0
MOV IP, #0
; Repeating this with enc_pw give us ;
; FROM enc_pw:
MOV R0, R8 ; R8 -> R0, R0 = 8192
MOV R1, SB ; R1 = SB, Static base register. This is a synonym for R9. #R1 = R9 = m. (we should read final result from here)
MOV R2, SL ; R2 = SL, Stack Limit register. This is a synonym for R10. #R2 = R10 = e.length (input length)
MOV R3, #0
MOV R5, #0
LDRB R4, [R0] ; Load register byte. #0x14 here
CMP R5, #1 ; IS R5 = 1?
BNE #0x28
AND R6, R3, #3
ADD R4, R4, R6
ADD R4, R4, #6 ; 0x28 is here. If R5 is 0, come here
AND R5, R4, #1 ; R5 == 1 if R4 is odd.
STRB R4, [R1] ; store register byte
ADD R0, R0, #1
ADD R1, R1, #1
ADD R3, R3, #1
CMP R3, R2
BLT #0x14 ; Go back if R3 < input length. R3 is a counter
MOV R0, #0
MOV R1, #0
MOV R2, #0
MOV R3, #0
MOV R4, #0
MOV R5, #0
MOV R6, #0
MOV R7, #0
MOV SB, #0
MOV SL, #0
```
In order to find the required password, we do the following:
```
function findReqR6(){
var pw = stoh("XYzaSAAX_PBssisodjsal_sSUVWZYYYb"); //We found this string above, from get_pw();
var required = new Array();
for(var i = 0 ; i < pw.length; i ++ ){
var a = pw[i];
a = a - 5; // We do this to find out what the original input needs to be.
if(i & 1 == 1){
a = a + 3; // Because the test adds 5, and might sub 2 in some cases
} // according to the assembly above.
required[i] = a;
}
return required;
}
```
`htos(findReqR6())` Will give us ``` SWu_N?