Reverse - LA CTF 2026

2026. 3. 1. 20:47·

Flag finder

Bài cổ quá rồi nên mình không nhớ web đấy có gì, đại loại là nó chưa một file script.js mà mình thấy sú xong tải về

Script.js

const fullInput = document.getElementById('fullInput');
const find = document.getElementById('find');
const result = document.getElementById('result');
const len = 1919;
const theFlag = /^(?=(?=(?:.{91}\..{9})*(?:.{91}#.{9}){4}(?:.{91}\..{9})+(?:.{91}#.{9}){5}(?:.{91}\..{9})+(?:.{91}#.{9}){4}(?:.{91}\..{9})*)(?=(?:.{52}\..{48}){19})(?=(?:.{61}\..{39})*(?:.{61}#.{39}){4}(?:.{61}\..{39})+(?:.{61}#.{39})(?:.{61}\..{39})+(?:.{61}#.{39})(?:.{61}\..{39})+(?:.{61}#.{39})(?:.{61}\..{39})+(?:.{61}#.{39}){4}(?:.{61}\..{39})*)(?=(?:.{11}\..{89})*(?:.{11}#.{89})(?:.{11}\..{89})+(?:.{11}#.{89})(?:.{11}\..{89})+(?:.{11}#.{89}){10}(?:.{11}\..{89})*)(?=(?:\..{100}){19})(?=(?:.{56}\..{44}){19})(?=(?:.{40}\..{60}){19})(?=(?:.{26}\..{74})*(?:.{26}#.{74}){2}(?:.{26}\..{74})+(?:.{26}#.{74})(?:.{26}\..{74})+(?:.{26}#.{74})(?:.{26}\..{74})+(?:.{26}#.{74})(?:.{26}\..{74})*)(?=(?:.{23}\..{77})*(?:.{23}#.{77})(?:.{23}\..{77})+(?:.{23}#.{77})(?:.{23}\..{77})+(?:.{23}#.{77})(?:.{23}\..{77})+(?:.{23}#.{77})(?:.{23}\..{77})+(?:.{23}#.{77}){3}(?:.{23}\..{77})*)(?=(?:.{42}\..{58})*(?:.{42}#.{58})(?:.{42}\..{58})+(?:.{42}#.{58})(?:.{42}\..{58})+(?:.{42}#.{58})(?:.{42}\..{58})+(?:.{42}#.{58})(?:.{42}\..{58})+(?:.{42}#.{58})(?:.{42}\..{58})*)(?=(?:.{31}\..{69})*(?:.{31}#.{69}){3}(?:.{31}\..{69})+(?:.{31}#.{69}){2}(?:.{31}\..{69})+(?:.{31}#.{69}){5}(?:.{31}\..{69})*)(?=(?:.{79}\..{21})*(?:.{79}#.{21}){5}(?:.{79}\..{21})+(?:.{79}#.{21})(?:.{79}\..{21})+(?:.{79}#.{21})(?:.{79}\..{21})+(?:.{79}#.{21}){3}(?:.{79}\..{21})*)(?=(?:.{44}\..{56}){19})(?=(?:.{65}\..{35})*(?:.{65}#.{35}){3}(?:.{65}\..{35})+(?:.{65}#.{35}){4}(?:.{65}\..{35})+(?:.{65}#.{35})(?:.{65}\..{35})+(?:.{65}#.{35})(?:.{65}\..{35})+(?:.{65}#.{35})(?:.{65}\..{35})*)(?=(?:.{30}\..{70})*(?:.{30}#.{70})(?:.{30}\..{70})+(?:.{30}#.{70})(?:.{30}\..{70})+(?:.{30}#.{70})(?:.{30}\..{70})*)(?=(?:.{38}\..{62})*(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})+(?:.{38}#.{62})(?:.{38}\..{62})*)(?=(?:.{37}\..{63})*(?:.{37}#.{63})(?:.{37}\..{63})+(?:.{37}#.{63}){3}(?:.{37}\..{63})+(?:.{37}#.{63})(?:.{37}\..{63})+(?:.{37}#.{63})(?:.{37}\..{63})*)(?=(?:.{27}\..{73})*(?:.{27}#.{73}){5}(?:.{27}\..{73})+(?:.{27}#.{73})(?:.{27}\..{73})+(?:.{27}#.{73})(?:.{27}\..{73})+(?:.{27}#.{73}){2}(?:.{27}\..{73})*)(?=(?:.{63}\..{37})*(?:.{63}#.{37}){5}(?:.{63}\..{37})+(?:.{63}#.{37}){5}(?:.{63}\..{37})+(?:.{63}#.{37})(?:.{63}\..{37})+(?:.{63}#.{37}){3}(?:.{63}\..{37})*)(?=(?:.{68}\..{32}){19})(?=(?:.{33}\..{67})*(?:.{33}#.{67}){3}(?:.{33}\..{67})+(?:.{33}#.{67}){4}(?:.{33}\..{67})+(?:.{33}#.{67}){4}(?:.{33}\..{67})*)(?=(?:.{53}\..{47})*(?:.{53}#.{47})(?:.{53}\..{47})+(?:.{53}#.{47})(?:.{53}\..{47})+(?:.{53}#.{47}){4}(?:.{53}\..{47})*)(?=(?:.{92}\..{8}){19})(?=(?:.{98}\..{2})*(?:.{98}#.{2})(?:.{98}\..{2})+(?:.{98}#.{2})(?:.{98}\..{2})+(?:.{98}#.{2})(?:.{98}\..{2})+(?:.{98}#.{2})(?:.{98}\..{2})+(?:.{98}#.{2}){2}(?:.{98}\..{2})+(?:.{98}#.{2}){2}(?:.{98}\..{2})*)(?=(?:.{29}\..{71})*(?:.{29}#.{71}){5}(?:.{29}\..{71})+(?:.{29}#.{71}){4}(?:.{29}\..{71})+(?:.{29}#.{71}){3}(?:.{29}\..{71})*)(?=(?:.{100}\.){19})(?=(?:.{58}\..{42})*(?:.{58}#.{42})(?:.{58}\..{42})+(?:.{58}#.{42})(?:.{58}\..{42})+(?:.{58}#.{42})(?:.{58}\..{42})+(?:.{58}#.{42})(?:.{58}\..{42})+(?:.{58}#.{42})(?:.{58}\..{42})+(?:.{58}#.{42})(?:.{58}\..{42})*)(?=(?:.{66}\..{34})*(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})+(?:.{66}#.{34})(?:.{66}\..{34})*)(?=(?:.{7}\..{93})*(?:.{7}#.{93}){3}(?:.{7}\..{93})+(?:.{7}#.{93})(?:.{7}\..{93})+(?:.{7}#.{93}){3}(?:.{7}\..{93})*)(?=(?:.{70}\..{30})*(?:.{70}#.{30})(?:.{70}\..{30})+(?:.{70}#.{30})(?:.{70}\..{30})+(?:.{70}#.{30})(?:.{70}\..{30})+(?:.{70}#.{30})(?:.{70}\..{30})+(?:.{70}#.{30}){2}(?:.{70}\..{30})*)(?=(?:.{80}\..{20}){19})(?=(?:.{25}\..{75})*(?:.{25}#.{75}){5}(?:.{25}\..{75})+(?:.{25}#.{75}){2}(?:.{25}\..{75})+(?:.{25}#.{75}){4}(?:.{25}\..{75})*)(?=(?:.{85}\..{15})*(?:.{85}#.{15})(?:.{85}\..{15})+(?:.{85}#.{15}){4}(?:.{85}\..{15})+(?:.{85}#.{15}){3}(?:.{85}\..{15})*)(?=(?:.{54}\..{46})*(?:.{54}#.{46})(?:.{54}\..{46})+(?:.{54}#.{46})(?:.{54}\..{46})+(?:.{54}#.{46})(?:.{54}\..{46})*)(?=(?:.{60}\..{40}){19})(?=(?:.{87}\..{13})*(?:.{87}#.{13})(?:.{87}\..{13})+(?:.{87}#.{13}){3}(?:.{87}\..{13})+(?:.{87}#.{13}){5}(?:.{87}\..{13})*)(?=(?:.{57}\..{43})*(?:.{57}#.{43}){3}(?:.{57}\..{43})+(?:.{57}#.{43})(?:.{57}\..{43})+(?:.{57}#.{43}){4}(?:.{57}\..{43})+(?:.{57}#.{43})(?:.{57}\..{43})+(?:.{57}#.{43})(?:.{57}\..{43})+(?:.{57}#.{43})(?:.{57}\..{43})*)(?=(?:.{20}\..{80}){19})(?=(?:.{96}\..{4}){19})(?=(?:.{17}\..{83})*(?:.{17}#.{83})(?:.{17}\..{83})+(?:.{17}#.{83}){3}(?:.{17}\..{83})+(?:.{17}#.{83}){4}(?:.{17}\..{83})*)(?=(?:.{46}\..{54})*(?:.{46}#.{54})(?:.{46}\..{54})+(?:.{46}#.{54})(?:.{46}\..{54})+(?:.{46}#.{54})(?:.{46}\..{54})+(?:.{46}#.{54})(?:.{46}\..{54})*)(?=(?:.{28}\..{72}){19})(?=(?:.{8}\..{92}){19})(?=(?:.{67}\..{33})*(?:.{67}#.{33}){4}(?:.{67}\..{33})+(?:.{67}#.{33})(?:.{67}\..{33})+(?:.{67}#.{33}){3}(?:.{67}\..{33})+(?:.{67}#.{33}){5}(?:.{67}\..{33})*)(?=(?:.{73}\..{27})*(?:.{73}#.{27}){4}(?:.{73}\..{27})+(?:.{73}#.{27})(?:.{73}\..{27})+(?:.{73}#.{27})(?:.{73}\..{27})+(?:.{73}#.{27}){4}(?:.{73}\..{27})*)(?=(?:.{16}\..{84}){19})(?=(?:.{94}\..{6})*(?:.{94}#.{6})(?:.{94}\..{6})+(?:.{94}#.{6})(?:.{94}\..{6})+(?:.{94}#.{6}){3}(?:.{94}\..{6})+(?:.{94}#.{6})(?:.{94}\..{6})*)(?=(?:.{2}\..{98})*(?:.{2}#.{98}){5}(?:.{2}\..{98})+(?:.{2}#.{98})(?:.{2}\..{98})+(?:.{2}#.{98})(?:.{2}\..{98})*)(?=(?:.{21}\..{79})*(?:.{21}#.{79}){3}(?:.{21}\..{79})+(?:.{21}#.{79})(?:.{21}\..{79})+(?:.{21}#.{79}){4}(?:.{21}\..{79})*)(?=(?:.{81}\..{19})*(?:.{81}#.{19})(?:.{81}\..{19})+(?:.{81}#.{19}){3}(?:.{81}\..{19})+(?:.{81}#.{19}){4}(?:.{81}\..{19})*)(?=(?:.{49}\..{51})*(?:.{49}#.{51}){4}(?:.{49}\..{51})+(?:.{49}#.{51}){3}(?:.{49}\..{51})+(?:.{49}#.{51})(?:.{49}\..{51})*)(?=(?:.{84}\..{16}){19})(?=(?:.{77}\..{23})*(?:.{77}#.{23})(?:.{77}\..{23})+(?:.{77}#.{23})(?:.{77}\..{23})+(?:.{77}#.{23})(?:.{77}\..{23})+(?:.{77}#.{23})(?:.{77}\..{23})+(?:.{77}#.{23}){4}(?:.{77}\..{23})*)(?=(?:.{62}\..{38})*(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})+(?:.{62}#.{38})(?:.{62}\..{38})*)(?=(?:.{5}\..{95})*(?:.{5}#.{95})(?:.{5}\..{95})+(?:.{5}#.{95}){2}(?:.{5}\..{95})+(?:.{5}#.{95})(?:.{5}\..{95})+(?:.{5}#.{95}){4}(?:.{5}\..{95})*)(?=(?:.{90}\..{10})*(?:.{90}#.{10}){3}(?:.{90}\..{10})+(?:.{90}#.{10})(?:.{90}\..{10})+(?:.{90}#.{10})(?:.{90}\..{10})+(?:.{90}#.{10}){3}(?:.{90}\..{10})*)(?=(?:.{76}\..{24}){19})(?=(?:.{47}\..{53})*(?:.{47}#.{53}){5}(?:.{47}\..{53})+(?:.{47}#.{53})(?:.{47}\..{53})+(?:.{47}#.{53}){5}(?:.{47}\..{53})*)(?=(?:.{74}\..{26})*(?:.{74}#.{26})(?:.{74}\..{26})+(?:.{74}#.{26})(?:.{74}\..{26})+(?:.{74}#.{26})(?:.{74}\..{26})+(?:.{74}#.{26}){2}(?:.{74}\..{26})+(?:.{74}#.{26})(?:.{74}\..{26})+(?:.{74}#.{26})(?:.{74}\..{26})*)(?=(?:.{43}\..{57})*(?:.{43}#.{57})(?:.{43}\..{57})+(?:.{43}#.{57})(?:.{43}\..{57})+(?:.{43}#.{57}){2}(?:.{43}\..{57})+(?:.{43}#.{57})(?:.{43}\..{57})*)(?=(?:.{39}\..{61})*(?:.{39}#.{61}){5}(?:.{39}\..{61})+(?:.{39}#.{61})(?:.{39}\..{61})+(?:.{39}#.{61}){2}(?:.{39}\..{61})+(?:.{39}#.{61}){2}(?:.{39}\..{61})*)(?=(?:.{32}\..{68}){19})(?=(?:.{86}\..{14})*(?:.{86}#.{14})(?:.{86}\..{14})+(?:.{86}#.{14})(?:.{86}\..{14})+(?:.{86}#.{14})(?:.{86}\..{14})*)(?=(?:.{55}\..{45})*(?:.{55}#.{45})(?:.{55}\..{45})+(?:.{55}#.{45})(?:.{55}\..{45})+(?:.{55}#.{45}){2}(?:.{55}\..{45})*)(?=(?:.{14}\..{86})*(?:.{14}#.{86}){5}(?:.{14}\..{86})+(?:.{14}#.{86})(?:.{14}\..{86})+(?:.{14}#.{86})(?:.{14}\..{86})+(?:.{14}#.{86})(?:.{14}\..{86})*)(?=(?:.{89}\..{11})*(?:.{89}#.{11}){4}(?:.{89}\..{11})+(?:.{89}#.{11}){2}(?:.{89}\..{11})+(?:.{89}#.{11}){4}(?:.{89}\..{11})*)(?=(?:.{15}\..{85})*(?:.{15}#.{85})(?:.{15}\..{85})+(?:.{15}#.{85})(?:.{15}\..{85})+(?:.{15}#.{85}){5}(?:.{15}\..{85})+(?:.{15}#.{85}){3}(?:.{15}\..{85})*)(?=(?:.{82}\..{18})*(?:.{82}#.{18})(?:.{82}\..{18})+(?:.{82}#.{18})(?:.{82}\..{18})+(?:.{82}#.{18})(?:.{82}\..{18})*)(?=(?:.{45}\..{55})*(?:.{45}#.{55}){2}(?:.{45}\..{55})+(?:.{45}#.{55})(?:.{45}\..{55})+(?:.{45}#.{55}){3}(?:.{45}\..{55})*)(?=(?:.{83}\..{17})*(?:.{83}#.{17}){5}(?:.{83}\..{17})+(?:.{83}#.{17}){5}(?:.{83}\..{17})+(?:.{83}#.{17}){2}(?:.{83}\..{17})*)(?=(?:.{13}\..{87})*(?:.{13}#.{87})(?:.{13}\..{87})+(?:.{13}#.{87}){4}(?:.{13}\..{87})+(?:.{13}#.{87}){4}(?:.{13}\..{87})*)(?=(?:.{71}\..{29})*(?:.{71}#.{29})(?:.{71}\..{29})+(?:.{71}#.{29}){5}(?:.{71}\..{29})+(?:.{71}#.{29})(?:.{71}\..{29})+(?:.{71}#.{29})(?:.{71}\..{29})*)(?=(?:.{19}\..{81})*(?:.{19}#.{81})(?:.{19}\..{81})+(?:.{19}#.{81})(?:.{19}\..{81})+(?:.{19}#.{81}){4}(?:.{19}\..{81})+(?:.{19}#.{81}){5}(?:.{19}\..{81})*)(?=(?:.{6}\..{94})*(?:.{6}#.{94}){2}(?:.{6}\..{94})+(?:.{6}#.{94})(?:.{6}\..{94})+(?:.{6}#.{94})(?:.{6}\..{94})+(?:.{6}#.{94})(?:.{6}\..{94})*)(?=(?:.{88}\..{12}){19})(?=(?:.{93}\..{7})*(?:.{93}#.{7}){5}(?:.{93}\..{7})+(?:.{93}#.{7})(?:.{93}\..{7})*)(?=(?:.{22}\..{78})*(?:.{22}#.{78}){2}(?:.{22}\..{78})+(?:.{22}#.{78}){2}(?:.{22}\..{78})+(?:.{22}#.{78})(?:.{22}\..{78})+(?:.{22}#.{78})(?:.{22}\..{78})+(?:.{22}#.{78})(?:.{22}\..{78})+(?:.{22}#.{78})(?:.{22}\..{78})*)(?=(?:.{18}\..{82})*(?:.{18}#.{82}){5}(?:.{18}\..{82})+(?:.{18}#.{82})(?:.{18}\..{82})+(?:.{18}#.{82})(?:.{18}\..{82})+(?:.{18}#.{82})(?:.{18}\..{82})*)(?=(?:.{34}\..{66})*(?:.{34}#.{66})(?:.{34}\..{66})+(?:.{34}#.{66})(?:.{34}\..{66})+(?:.{34}#.{66})(?:.{34}\..{66})+(?:.{34}#.{66}){3}(?:.{34}\..{66})*)(?=(?:.{24}\..{76}){19})(?=(?:.{4}\..{96}){19})(?=(?:.{36}\..{64}){19})(?=(?:.{3}\..{97})*(?:.{3}#.{97})(?:.{3}\..{97})+(?:.{3}#.{97}){3}(?:.{3}\..{97})+(?:.{3}#.{97})(?:.{3}\..{97})*)(?=(?:.{97}\..{3})*(?:.{97}#.{3})(?:.{97}\..{3})+(?:.{97}#.{3})(?:.{97}\..{3})+(?:.{97}#.{3})(?:.{97}\..{3})+(?:.{97}#.{3}){3}(?:.{97}\..{3})+(?:.{97}#.{3})(?:.{97}\..{3})+(?:.{97}#.{3})(?:.{97}\..{3})*)(?=(?:.{95}\..{5})*(?:.{95}#.{5}){3}(?:.{95}\..{5})+(?:.{95}#.{5})(?:.{95}\..{5})*)(?=(?:.\..{99})*(?:.#.{99})(?:.\..{99})+(?:.#.{99})(?:.\..{99})+(?:.#.{99}){4}(?:.\..{99})+(?:.#.{99})(?:.\..{99})*)(?=(?:.{48}\..{52}){19})(?=(?:.{12}\..{88}){19})(?=(?:.{75}\..{25})*(?:.{75}#.{25})(?:.{75}\..{25})+(?:.{75}#.{25}){3}(?:.{75}\..{25})+(?:.{75}#.{25})(?:.{75}\..{25})+(?:.{75}#.{25})(?:.{75}\..{25})+(?:.{75}#.{25}){5}(?:.{75}\..{25})*)(?=(?:.{59}\..{41})*(?:.{59}#.{41}){5}(?:.{59}\..{41})+(?:.{59}#.{41}){2}(?:.{59}\..{41})+(?:.{59}#.{41}){5}(?:.{59}\..{41})*)(?=(?:.{69}\..{31})*(?:.{69}#.{31})(?:.{69}\..{31})+(?:.{69}#.{31})(?:.{69}\..{31})+(?:.{69}#.{31})(?:.{69}\..{31})+(?:.{69}#.{31})(?:.{69}\..{31})+(?:.{69}#.{31})(?:.{69}\..{31})+(?:.{69}#.{31})(?:.{69}\..{31})*)(?=(?:.{51}\..{49})*(?:.{51}#.{49}){5}(?:.{51}\..{49})+(?:.{51}#.{49}){5}(?:.{51}\..{49})+(?:.{51}#.{49})(?:.{51}\..{49})*)(?=(?:.{72}\..{28}){19})(?=(?:.{50}\..{50})*(?:.{50}#.{50})(?:.{50}\..{50})+(?:.{50}#.{50})(?:.{50}\..{50})+(?:.{50}#.{50})(?:.{50}\..{50})+(?:.{50}#.{50})(?:.{50}\..{50})*)(?=(?:.{78}\..{22})*(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})+(?:.{78}#.{22})(?:.{78}\..{22})*)(?=(?:.{10}\..{90})*(?:.{10}#.{90})(?:.{10}\..{90})+(?:.{10}#.{90})(?:.{10}\..{90})+(?:.{10}#.{90})(?:.{10}\..{90})+(?:.{10}#.{90}){2}(?:.{10}\..{90})+(?:.{10}#.{90})(?:.{10}\..{90})*)(?=(?:.{99}\..)*(?:.{99}#.){5}(?:.{99}\..)+(?:.{99}#.){5}(?:.{99}\..)+(?:.{99}#.){3}(?:.{99}\..)*)(?=(?:.{35}\..{65})*(?:.{35}#.{65}){5}(?:.{35}\..{65})+(?:.{35}#.{65}){5}(?:.{35}\..{65})+(?:.{35}#.{65}){4}(?:.{35}\..{65})*)(?=(?:.{41}\..{59})*(?:.{41}#.{59})(?:.{41}\..{59})+(?:.{41}#.{59}){3}(?:.{41}\..{59})+(?:.{41}#.{59})(?:.{41}\..{59})+(?:.{41}#.{59})(?:.{41}\..{59})*)(?=(?:.{9}\..{91})*(?:.{9}#.{91}){2}(?:.{9}\..{91})+(?:.{9}#.{91}){3}(?:.{9}\..{91})+(?:.{9}#.{91})(?:.{9}\..{91})+(?:.{9}#.{91}){4}(?:.{9}\..{91})*)(?=(?:.{64}\..{36}){19}))(?=^.{1919}$)(\.*)(?<=.{101})(?<!.{102})(\.*#{2}\.+#\.+#{2}\.+#{2}\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#{2}\.+#{2}\.+#{2}\.+#{3}\.+#{3}\.+#\.+#{3}\.*)(?<=.{202})(?<!.{203})(\.*#\.+#{2}\.+#{2}\.+#{3}\.+#\.+#{2}\.+#\.+#\.+#{2}\.+#\.+#\.+#\.+#{2}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{2}\.+#\.*)(?<=.{303})(?<!.{304})(\.*#\.+#{2}\.+#\.+#\.+#{3}\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#{3}\.+#\.+#{3}\.+#\.+#\.+#{3}\.*)(?<=.{404})(?<!.{405})(\.*#\.+#\.+#\.+#\.+#\.+#\.+#{2}\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#\.*)(?<=.{505})(?<!.{506})(\.*#{3}\.+#{3}\.+#{2}\.+#{2}\.+#\.+#{2}\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#{2}\.+#{3}\.+#{3}\.+#\.+#{3}\.+#{2}\.+#{3}\.+#{3}\.+#{3}\.+#\.+#{3}\.+#{3}\.+#\.+#\.+#{3}\.*)(?<=.{606})(?<!.{607})(\.*#{3}\.*)(?<=.{707})(?<!.{708})(\.*#{2}\.+#{2}\.+#{3}\.+#{3}\.+#\.+#\.+#{3}\.+#{2}\.+#{3}\.+#\.+#\.+#\.+#\.+#\.*)(?<=.{808})(?<!.{809})(\.*#{2}\.+#\.+#\.+#\.+#\.+#\.+#\.+#{2}\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{2}\.+#{2}\.+#\.+#\.*)(?<=.{909})(?<!.{910})(\.*#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#{3}\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#{3}\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#{3}\.*)(?<=.{1010})(?<!.{1011})(\.*#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.*)(?<=.{1111})(?<!.{1112})(\.*#\.+#\.+#{3}\.+#\.+#{3}\.+#{2}\.+#{3}\.+#{2}\.+#\.+#{3}\.+#{2}\.+#{2}\.+#{3}\.+#\.+#{3}\.+#\.+#{3}\.+#{3}\.+#{3}\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#{2}\.+#{3}\.+#\.*)(?<=.{1212})(?<!.{1213})(\.*#{3}\.*)(?<=.{1313})(?<!.{1314})(\.*#{2}\.+#{2}\.+#{2}\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#{2}\.+#{3}\.+#{2}\.+#{2}\.+#\.+#\.+#\.+#{2}\.*)(?<=.{1414})(?<!.{1415})(\.*#{2}\.+#\.+#\.+#{2}\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#\.+#{2}\.*)(?<=.{1515})(?<!.{1516})(\.*#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#{3}\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#\.+#\.*)(?<=.{1616})(?<!.{1617})(\.*#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#{2}\.*)(?<=.{1717})(?<!.{1718})(\.*#{3}\.+#\.+#\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#{3}\.+#\.+#{3}\.+#\.+#{3}\.+#{3}\.+#{3}\.+#\.+#\.+#{3}\.+#{3}\.+#\.+#\.+#\.+#\.+#\.+#{2}\.*)(?<=.{1818})(?<!.{1819})(\.*)(?<=.{1919})(?<!.{1920})$/; 
function createInput() {
    for (let i = 0; i < len; i++) {
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.id = `box-${i}`;
        checkbox.className = 'grid-box';
        fullInput.appendChild(checkbox);
    }
}
function match() {
    const boxes = document.querySelectorAll('.grid-box');
    let input = "";
    boxes.forEach(box => {
        input += box.checked ? "#" : ".";
    });
    result.className = 'status';
    if (theFlag.test(input)) {
        result.textContent = "✅ Flag found!";
        result.classList.add('success');
    } else {
        result.textContent = "❌ Flag not found.";
        result.classList.add('error');
    }
}
createInput();
find.addEventListener('click', match);

Script này sẽ đơn giản là vẽ hình ra với ký tự . và #

Hàm này nhận những mảnh như:

  • (?:.{91}\..{9})
  • (?:.{91}#.{9})
  • (?:\..{100})
  • (?:.{100}\.)

và hiểu chúng là:

“Trong một hàng dài đúng 101 ký tự, ở vị trí cột pre_len thì ký tự phải là . hoặc #.”

Script để vẽ lại hình theo yêu cầu của file js

#!/usr/bin/env python3
import re
import sys
from functools import lru_cache
from PIL import Image

COLS = 101  # from CSS repeat(101,...)

def extract_regex_and_len(js: str):
    mlen = re.search(r"const\s+len\s*=\s*(\d+)\s*;", js)
    if not mlen:
        raise ValueError("Cannot find const len = ...;")
    total_len = int(mlen.group(1))

    # non-greedy capture between / ... /;
    m = re.search(r"const\s+theFlag\s*=\s*/(.*?)/\s*;", js, re.S)
    if not m:
        raise ValueError("Cannot find const theFlag = /.../;")
    regex_src = m.group(1)
    return regex_src, total_len

def extract_lookaheads(s: str):
    """
    Return ALL lookaheads '(?=...)' including nested ones.
    """
    out = []

    def find_end(i: int) -> int:
        # i points to '(' of '(?='
        depth = 0
        j = i
        while j < len(s):
            ch = s[j]
            if ch == '\\':  # skip escaped char
                j += 2
                continue
            if ch == '(':
                depth += 1
            elif ch == ')':
                depth -= 1
                if depth == 0:
                    return j
            j += 1
        raise ValueError("Unbalanced parentheses while parsing lookaheads")

    def walk(lo: int, hi: int):
        i = lo
        while i < hi:
            if s.startswith("(?=", i):
                end = find_end(i)
                out.append(s[i:end+1])
                # recurse inside the lookahead body
                walk(i + 3, end)   # content inside '(?=' ... ')'
                i = end + 1
            else:
                i += 1

    walk(0, len(s))
    return out

def parse_chunk_group(g: str):
    """
    Parse chunk like (?:.{91}\..{9}) or (?:\..{100}) or (?:.\..{99}) or (?:.{100}\.)
    and same with # in place of \.
    Return (col_index, sym) where sym in {'.','#'} if it is exactly one row-chunk of length 101.
    """
    if not (g.startswith("(?:") and g.endswith(")")):
        return None
    inner = g[3:-1]

    # prefix: "", "." or ".{n}"
    # sym: "\." or "#"
    # suffix: "", "." or ".{n}"
    m = re.fullmatch(r"(?:(?:\.\{(\d+)\})|(\.))?(\\\.|#)(?:(?:\.\{(\d+)\})|(\.))?", inner)
    if not m:
        return None
    pre_n, pre_dot, sym, suf_n, suf_dot = m.groups()
    pre_len = int(pre_n) if pre_n else (1 if pre_dot else 0)
    suf_len = int(suf_n) if suf_n else (1 if suf_dot else 0)
    if pre_len + 1 + suf_len != COLS:
        return None
    col = pre_len
    sym_char = '.' if sym == r'\.' else '#'
    return col, sym_char

def parse_column_constraint(look: str):
    """
    Try interpret a lookahead as a single-column vertical nonogram constraint.
    Return (col_index, clues_list) or None.
    """
    # Extract all (?:...) groups that parse as row-chunks; must all point to same column.
    cols = set()
    dot_chunk = None
    hash_chunk = None

    i = 0
    while i < len(look):
        if look.startswith("(?:", i):
            depth = 0
            j = i
            while j < len(look):
                ch = look[j]
                if ch == '\\':
                    j += 2
                    continue
                if ch == '(':
                    depth += 1
                elif ch == ')':
                    depth -= 1
                    if depth == 0:
                        grp = look[i:j+1]
                        pc = parse_chunk_group(grp)
                        if pc:
                            col, sym = pc
                            cols.add(col)
                            if sym == '.':
                                dot_chunk = grp
                            else:
                                hash_chunk = grp
                        i = j + 1
                        break
                j += 1
            else:
                break
        else:
            i += 1

    if not cols or len(cols) != 1:
        return None
    col = next(iter(cols))

    # Dot-only column constraint → no hash runs
    if hash_chunk is None and dot_chunk is not None:
        return col, []

    if not dot_chunk or not hash_chunk:
        return None

    # Count hash runs from occurrences of the hash chunk followed by optional {n}
    hs = re.escape(hash_chunk)
    clues = []
    for m in re.finditer(hs + r"(\{(\d+)\})?", look):
        n = int(m.group(2)) if m.group(2) else 1
        clues.append(n)

    return col, clues

def extract_row_clues(regex_src: str, total_len: int, rows: int):
    marker = f"(?=^.{{{total_len}}}$)"
    if marker not in regex_src:
        raise ValueError("Cannot find length marker in regex")

    _, suffix = regex_src.split(marker, 1)

    # Row 0 is the very first capturing group: (\.*)  (or generally: (<no ')'>))
    m0 = re.match(r"\(([^)]*)\)", suffix)
    if not m0:
        raise ValueError("Cannot find leading row-0 group after length marker")
    row_patts = [m0.group(1)]
    pos = m0.end()

    # Rows 1..rows-1 are: (?<=.{k})(?<!.{k+1})(...)
    for i in range(1, rows):
        k = i * COLS
        pat = re.compile(rf"\(\?<=\.\{{{k}\}}\)\(\?<\!\.\{{{k+1}\}}\)\(([^)]*)\)")
        m = pat.search(suffix, pos)
        if not m:
            raise ValueError(f"Cannot find row {i} group at boundary {k}")
        row_patts.append(m.group(1))
        pos = m.end()

    if len(row_patts) != rows:
        raise ValueError(f"Expected {rows} row-patterns, got {len(row_patts)}")

    row_clues = []
    for patt in row_patts:
        blocks = []
        for t in re.finditer(r"#\{(\d+)\}|#", patt):
            blocks.append(int(t.group(1)) if t.group(1) else 1)
        row_clues.append(blocks)

    return row_clues

def extract_col_clues(regex_src: str, total_len: int, cols: int):
    marker = f"(?=^.{{{total_len}}}$)"
    prefix, _ = regex_src.split(marker, 1)
    looks = extract_lookaheads(prefix)

    colmap = {}
    for lk in looks:
        parsed = parse_column_constraint(lk)
        if not parsed:
            continue
        c, clues = parsed
        # Keep the “most informative” one if duplicates show up
        if c not in colmap or len(clues) > len(colmap[c]):
            colmap[c] = clues

    if len(colmap) != cols:
        missing = [c for c in range(cols) if c not in colmap]
        raise ValueError(f"Missing column constraints for {len(missing)} cols (e.g. {missing[:10]})")

    return [colmap[c] for c in range(cols)]

def line_analyze(L, clues, fixed):
    """
    fixed: list of -1 (unknown), 0 (dot), 1 (hash)
    returns: feasible, forced(list -1/0/1)
    """
    k = len(clues)
    fixed = list(fixed)

    def trans(state, ch):
        idx, run_left, must_dot = state
        if run_left > 0:
            if ch != '#':
                return None
            if run_left == 1:
                new_idx = idx + 1
                new_must = (new_idx < k)
                return (new_idx, 0, new_must)
            return (idx, run_left - 1, False)
        else:
            if must_dot:
                if ch != '.':
                    return None
                return (idx, 0, False)
            else:
                if ch == '.':
                    return (idx, 0, False)
                if ch == '#':
                    if idx >= k:
                        return None
                    length = clues[idx]
                    if length == 1:
                        new_idx = idx + 1
                        new_must = (new_idx < k)
                        return (new_idx, 0, new_must)
                    return (idx, length - 1, False)
        return None

    start = (0, 0, False)
    accept = (k, 0, False)

    fwd = [set() for _ in range(L+1)]
    fwd[0].add(start)
    for pos in range(L):
        allowed = []
        if fixed[pos] != 1:
            allowed.append('.')
        if fixed[pos] != 0:
            allowed.append('#')
        for st in fwd[pos]:
            for ch in allowed:
                ns = trans(st, ch)
                if ns is not None:
                    fwd[pos+1].add(ns)

    if accept not in fwd[L]:
        return False, [-1] * L

    bwd = [set() for _ in range(L+1)]
    bwd[L].add(accept)
    for pos in range(L-1, -1, -1):
        allowed = []
        if fixed[pos] != 1:
            allowed.append('.')
        if fixed[pos] != 0:
            allowed.append('#')
        for st in fwd[pos]:
            for ch in allowed:
                ns = trans(st, ch)
                if ns is not None and ns in bwd[pos+1]:
                    bwd[pos].add(st)
                    break

    can0 = [False] * L
    can1 = [False] * L
    for pos in range(L):
        for st in fwd[pos]:
            if st not in bwd[pos]:
                continue
            if fixed[pos] != 1:
                ns = trans(st, '.')
                if ns is not None and ns in bwd[pos+1]:
                    can0[pos] = True
            if fixed[pos] != 0:
                ns = trans(st, '#')
                if ns is not None and ns in bwd[pos+1]:
                    can1[pos] = True
            if can0[pos] and can1[pos]:
                break

    forced = [-1] * L
    for i in range(L):
        if can1[i] and not can0[i]:
            forced[i] = 1
        elif can0[i] and not can1[i]:
            forced[i] = 0
    return True, forced

def solve_nonogram(row_clues, col_clues):
    R = len(row_clues)
    C = len(col_clues)
    grid = [[-1] * C for _ in range(R)]

    def propagate(g):
        changed = True
        while changed:
            changed = False
            for r in range(R):
                ok, forced = line_analyze(C, row_clues[r], g[r])
                if not ok:
                    return False
                for c, v in enumerate(forced):
                    if v != -1 and g[r][c] != v:
                        if g[r][c] != -1 and g[r][c] != v:
                            return False
                        g[r][c] = v
                        changed = True

            for c in range(C):
                col = [g[r][c] for r in range(R)]
                ok, forced = line_analyze(R, col_clues[c], col)
                if not ok:
                    return False
                for r, v in enumerate(forced):
                    if v != -1 and g[r][c] != v:
                        if g[r][c] != -1 and g[r][c] != v:
                            return False
                        g[r][c] = v
                        changed = True
        return True

    def complete(g):
        return all(x != -1 for row in g for x in row)

    def pick_unknown(g):
        for r in range(R):
            for c in range(C):
                if g[r][c] == -1:
                    return r, c
        return None

    def bt(g):
        if not propagate(g):
            return None
        if complete(g):
            return g
        r, c = pick_unknown(g)
        for v in (1, 0):
            ng = [row[:] for row in g]
            ng[r][c] = v
            sol = bt(ng)
            if sol is not None:
                return sol
        return None

    return bt(grid)

def render_ascii(grid):
    return "\n".join("".join("#" if v == 1 else "." for v in row) for row in grid)

def render_png(grid, scale=10, outpath="out.png"):
    R = len(grid)
    C = len(grid[0])
    img = Image.new("RGB", (C * scale, R * scale), (255, 255, 255))
    px = img.load()
    for r in range(R):
        for c in range(C):
            if grid[r][c] == 1:
                for y in range(r*scale, (r+1)*scale):
                    for x in range(c*scale, (c+1)*scale):
                        px[x, y] = (0, 0, 0)
    img.save(outpath)

def main():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} script.js")
        sys.exit(1)

    js = open(sys.argv[1], "r", encoding="utf-8", errors="replace").read()
    regex_src, total_len = extract_regex_and_len(js)

    rows = total_len // COLS
    if rows * COLS != total_len:
        raise ValueError(f"len={total_len} not divisible by COLS={COLS}")

    row_clues = extract_row_clues(regex_src, total_len, rows)
    col_clues = extract_col_clues(regex_src, total_len, COLS)

    grid = solve_nonogram(row_clues, col_clues)
    if grid is None:
        raise RuntimeError("No solution found")

    art = render_ascii(grid)
    open("out.txt", "w", encoding="utf-8").write(art + "\n")
    render_png(grid, scale=10, outpath="out.png")

    # Flatten to the exact input string (1919 chars) to auto-fill in browser
    flat = "".join("".join("#" if v == 1 else "." for v in row) for row in grid)
    open("solution.bin", "w", encoding="utf-8").write(flat)

    print("[+] Solved.")
    print("[+] Wrote out.txt, out.png, solution.bin")
    print("\n[+] Browser autofill snippet (paste in DevTools console):\n")
    print("(() => {")
    print("  const s = `" + flat + "`;")
    print("  for (let i=0;i<s.length;i++) {")
    print("    const el = document.getElementById(`box-${i}`);")
    print("    if (el) el.checked = (s[i] === '#');")
    print("  }")
    print("  console.log('filled');")
    print("})();\n")
    print("[+] Now click “Find flag” and read the text drawn on the grid (that text is the flag).")

if __name__ == "__main__":
    main()

Nó sẽ vẽ được một hình như sau

Flag: lactf{Wh47_d0_y0u_637_eh3n_y0u_cr055_4_r363x_4nd_4_n0n06r4m?_4_r3g3x06r4m!}

 

'WriteUp > RE' 카테고리의 다른 글

Rev - UofTCTF 2026  (0) 2026.01.13
Cosmonaut - BuckeyeCTF 2025  (0) 2025.11.09
Square Cipher - BuckeyeCTF 2025  (0) 2025.11.09
Python 0bf  (0) 2025.11.02
[RE] Clockwork - EnigmaXplore 3.0  (0) 2025.10.20
'WriteUp/RE' Other posts in category
  • Rev - UofTCTF 2026
  • Cosmonaut - BuckeyeCTF 2025
  • Square Cipher - BuckeyeCTF 2025
  • Python 0bf
longhd
longhd
Longhd's Blog
  • longhd
    Ha Duy Long - InfosecPTIT
    longhd
  • Total
    Today
    Yesterday
  • About me

    • Hello I'm Duy Long 👋🏻
    • View all categories (117) N
      • Certificates (4)
      • CTF (3)
      • WriteUp (94) N
        • Forensics (44) N
        • Steganography (5)
        • RE (9) N
        • OSINT (8)
        • Web (17)
        • MISC (6)
        • Crypto (3)
        • Pwn (2)
      • Love Story (0)
      • Labs (15)
        • Information Gathering (10)
        • Vulnerability Scanning (2)
        • Introduction to Web Applica.. (1)
        • Common Web Application Atta.. (1)
        • SQL Injection Attacks (1)
  • Blog Menu

    • Home
    • Tag
    • GuestBook
  • Popular Posts

  • Tags

    BuckeyeCTF2025
    SunshineCTF2025
    Re
    OSINT
    writeup
    picoCTF
    EnigmaXplore3.0
    htb
    CTF
    V1tCTF2025
    CSCV2025
    misc
    POCCTF2025
    Dreamhack
    Forensics
    Steganography
    THM
    CHH
    Web
    PTITCTF2025
  • Recent Comments

  • Recent Posts

  • hELLO· Designed ByLong.v4.10.4
longhd
Reverse - LA CTF 2026
Go to Top

티스토리툴바