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}
打赏还是打残,这是个问题
没有帐号? 立即注册