openwrt,MIPSEL架构,给了fs和内核,挂载fs提取固件程序/bin/hello,命令:mkdir load && mount -t ext4 -o loop openwrt-malta-le-root.ext4 load
用retdec反编译,整理程序逻辑,主函数的大概逻辑如下:
int main(int argc, char ** argv) {
...
puts("welcome to qwb2019");
puts("user_name: ");
int32_t username = &v1; // 0x40056c
scanf("%s", username);
puts("user_pass: ");
int32_t pwd = &v3; // 0x40058c
scanf("%s", pwd);
int32_t buf = malloc(80); // 0x400594
*buf = *username;
*(buf + 4) = *(username + 4);
*(buf + 8) = *(username + 8);
*(buf + 12) = *(username + 12);
int32_t v7 = username + 16; // 0x4005cc
int32_t v8 = buf + 16; // 0x4005d0
buf = v8;
...
if (check(buf, v14, v11, v10) == 0) {
// 0x400640
puts_rc = puts("\nno");
// branch -> 0x40064c
} else {
// 0x400660
puts("\nyes");
puts_rc = printf("flag{%.5s%.32s}\n\n", buf, v9); //user+pass
// branch -> 0x40064c
}
// 0x40064c
return puts_rc;
}需要输入用户名和密码,组合起来为最终的flag。其中可以知道用户名为5位,密码32位。
程序的验证在check函数中,主要分成了两部分:
int32_t check(int32_t a1, int32_t a2, int32_t a3, int32_t a4) {
// 0x400d48
int32_t result; // 0x400d74
if (check1((char *)a1) != 0) {
// 0x400d60
result = check2(a1) != 0;
// branch -> 0x400d6c
} else {
result = 0;
}
// 0x400d6c
return result;
}check1对用户名进行校验,逻辑如下,容易求得为,脚本见最后:
int32_t check1(char * a1) {
int32_t v1 = (int32_t)a1;
char v2 = *(char *)(v1 + 1); // 0x400c70
char v3 = *(char *)(v1 + 2); // 0x400c74
char v4 = *(char *)(v1 + 3); // 0x400c80
char v5 = *(char *)(v1 + 4); // 0x400c8c
int32_t v6 = ((int32_t)v2 ^ 54) + (0x1000000 * v1 / 0x1000000 ^ 99) + ((int32_t)v3 ^ 48) + ((int32_t)v4 ^ 100) + ((int32_t)v5 ^ 48); // 0x400ca0
int32_t v7 = v1 + 5; // 0x400cac
int32_t result = (int32_t)*(char *)v7 + v6; // 0x400cb8
// branch -> 0x400cac
while (v7 != v1 + 39) {
// 0x400cac
v7++;
result += (int32_t)*(char *)v7;
// continue -> 0x400cac
}
// 0x400cbc
return result;
}check2对密码进行校验,使用了vm,指令INS存放在0x4110C0处,6个字节一条指令语句,校验的密文CHECK存放在0x411040处:
INS = [ 0x08,0x00,0x00,0x00,0x20,0x00, 0x08,0x00,0x01,0x00,0x00,0x00, 0x08,0x00,0x02,0x00,0x01,0x00, 0x03,0x00,0x01,0x00,0x00,0x00, 0x04,0x01,0x16,0x00,0x00,0x00, 0x08,0x00,0x0B,0x00,0x00,0x00, 0x08,0x00,0x0C,0x00,0x00,0x00, 0x01,0x00,0x0C,0x00,0x0B,0x00, 0x03,0x00,0x0B,0x00,0x01,0x00, 0x01,0x00,0x0B,0x00,0x02,0x00, 0x04,0x02,0xFC,0xFF,0x00,0x00, 0x08,0x00,0x03,0x00,0x08,0x00, 0x08,0x00,0x04,0x00,0x06,0x00, 0x08,0x00,0x09,0x00,0x10,0x00, 0x0F,0x00,0x09,0x00,0x03,0x00, 0x08,0x00,0x0A,0x00,0x24,0x00, 0x01,0x00,0x09,0x00,0x0A,0x00, 0x01,0x00,0x09,0x00,0x0C,0x00, 0x0A,0x00,0x05,0x00,0x01,0x00, 0x0D,0x00,0x05,0x00,0x09,0x00, 0x05,0x00,0x06,0x00,0x05,0x00, 0x10,0x00,0x06,0x00,0x04,0x00, 0x0B,0x00,0x07,0x00,0x01,0x00, 0x03,0x00,0x06,0x00,0x07,0x00, 0x01,0x00,0x01,0x00,0x02,0x00, 0x04,0x01,0xE9,0xFF,0x00,0x00, 0x04,0x02,0x01,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,0x00,0x00, 0x09,0x00,0x00,0x00,0x00,0x00, 0xFF,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00]
得到最终校验的大致算法如下:
for i in range(32): j += i if (PWD[i]*(0x1024+j))>>6 == CHECK[i]: continue else: break
由于PWD为可见字符,爆破可以得到最终密码为
脚本:
CHECK=[0x00000C5B,0x00000CDD,0x00000D1F,0x000018C0,0x000018C6,0x00000C26,0x00000E72,0x00000DF7,0x000019B1,0x00000D41,0x00000D08,0x0000191C,0x00000CD9,0x00000EB1,0x00000CEE,0x00001A78,0x00000D8B,0x00000D99,0x00000D64,0x00000CED,0x000019F8,0x00000E61,0x00001A7F,0x00001AE7,0x00000F26,0x00001B34,0x00001AD0,0x00000D7C,0x00000FC9,0x00000E7E,0x00001C0E,0x00001BAE]
def check1():
user = chr(99)+chr(54)+chr(48)+chr(100)+chr(48)
return user
def check2():
pwd = ""
j = 0
for i in range(32):
j += i
for d in range(32,127):
if (d*(0x1024+j))>>6 == CHECK[i]:
pwd+=chr(d)
break
return pwd
user = check1()
pwd = check2()
print "flag{%s%s}"%(user,pwd)
# flag{c60d0134bb097e43b292f4431b6cd8db194db}
打赏还是打残,这是个问题


没有帐号? 立即注册