[WGMY] Stable
Task Description
This is very unstable, and they say python is a stable language
This challenge provided a Python file called stable.py
.
Please note that I did not solve this challenge during the competition, only solved it after the competition with the help of hints. Huge thanks to Catz for the hints.
Looking into the Python file, we can see it was obfuscated
arr = vars(__builtins__)
BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3 = arr["\x5f_\u0069\155p\u006fr\u0074\137_"]
LZOGJVLBQSWZO7XGEM4T5LXNT4DVYVTI3ITSTEDHBZNDE4ZCLYINMBPIP62 = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\151\u006esp\x65\U00000063t")
M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ = arr["v\x61\U00000072\x73"]
LQJ5TKYMCG4BOHSKBGROAB6LQAJIHIWDW2HHGCYR6CKT5YERSO6GNHYO2Z = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(LZOGJVLBQSWZO7XGEM4T5LXNT4DVYVTI3ITSTEDHBZNDE4ZCLYINMBPIP62)["\147\x65\164\x73\U0000006fu\U00000072\u0063e"]
HZAECZKTOPGGWPUXM6OIA3QEIJ5WVVLB3UY7A2WGCARMXYO5VW7IPQOL4A = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\U00000073\u0079s")
XWIIZVLEG5Q7Y6DFZVGHO7SS2H6QXSOZABY3MHW63THWNNF5K7QQ3T5SED = arr["\x70\162\u0069nt"]
ODZYEJDEIBAQOTUP265UQSON5XNQ6K6VU2JLJCE576B5BMAVMJ2GNSN3EH = LQJ5TKYMCG4BOHSKBGROAB6LQAJIHIWDW2HHGCYR6CKT5YERSO6GNHYO2Z(M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(HZAECZKTOPGGWPUXM6OIA3QEIJ5WVVLB3UY7A2WGCARMXYO5VW7IPQOL4A)["\x6d\U0000006f\144\x75\u006c\U00000065\163"]["_\U0000005f\u006d\141i\u006e\U0000005f_"])
YQZ5ROM4W3C3E7OPYI6BN4XSGG6CAR76JZYGEV73NP6DEUT5MTQ3IVZGVP = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(HZAECZKTOPGGWPUXM6OIA3QEIJ5WVVLB3UY7A2WGCARMXYO5VW7IPQOL4A)["\x61\U00000072g\x76"]
ZA5OAQIR5BNYAB6Q4HSLDPFBKIAW3TPT3YXHESXRX4LHMH4FZHTP4Y57RX = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(HZAECZKTOPGGWPUXM6OIA3QEIJ5WVVLB3UY7A2WGCARMXYO5VW7IPQOL4A)["\u0065\x78i\U00000074"]
ZWDPZJHUEX4LZK6TNL3X7ABBACBY2XZR4HQR6CDVOFLOOHRWAXZGNYD3OF = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\x72\u0065")
QI5FPMPNP3FNLBUTFG23VRTIRD3QURRB26DVM5X4GYYEDVFCBJRF3T3AEB = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(ZWDPZJHUEX4LZK6TNL3X7ABBACBY2XZR4HQR6CDVOFLOOHRWAXZGNYD3OF)["\x66\u0069\156\x64a\U0000006c\x6c"]
EBNIQQWVCVPSF2UM5T56XXII7MP4MEGGNICNWZ2WU6HXDQSCRRXJGHMPP6 = QI5FPMPNP3FNLBUTFG23VRTIRD3QURRB26DVM5X4GYYEDVFCBJRF3T3AEB('_?([^\n?\\[\\]\']+?)\\s=', ODZYEJDEIBAQOTUP265UQSON5XNQ6K6VU2JLJCE576B5BMAVMJ2GNSN3EH)
QRN64DOU74HGX6YQ7Y5QHLWT2E64375UTTAI3GDVQPJSPR2GZTQLJU6DYX = arr["s\164\u0072"]
K3SJIHD3YW5CHGVNKHB4BJ53ZJ6XIM3X2CZQECDLQNIOQNKLEZ5DS6MORP = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(QRN64DOU74HGX6YQ7Y5QHLWT2E64375UTTAI3GDVQPJSPR2GZTQLJU6DYX)["j\157\x69n"]
A7RQQ7PC4WLBAFY3PDAOUOCME5DZMEAR5ZEFBNN2PQKYIAWOPIAA5QSL2J = K3SJIHD3YW5CHGVNKHB4BJ53ZJ6XIM3X2CZQECDLQNIOQNKLEZ5DS6MORP("", EBNIQQWVCVPSF2UM5T56XXII7MP4MEGGNICNWZ2WU6HXDQSCRRXJGHMPP6)
A5CXS7GCTRQQV2G7SFIVU3TI5KZKLUK7TC2GQ7UKMWKPKI6GWR65RFHJK4 = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(QRN64DOU74HGX6YQ7Y5QHLWT2E64375UTTAI3GDVQPJSPR2GZTQLJU6DYX)["\154\x6au\u0073\164"]
GKIJDAGEEVN2IPUHBLY5OL2L2IY4RG64OTGMSMJDZQJMOPMW6L4QCDV7L2 = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\x6d\141\U00000074\x68")
M74NWW5W4BA35KFV6ROMY3XV4YASUZ733DEDSLTDLJTZUSQBDY7HYAFTVU = arr["\U0000006cen"]
P2VMOBU2U7GTIITCNEWRKSATW3CVBQJ5BZKSFEDWDWTJAAFI34KLK7U32U = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(GKIJDAGEEVN2IPUHBLY5OL2L2IY4RG64OTGMSMJDZQJMOPMW6L4QCDV7L2)["\x63e\U00000069\x6c"]
XMW3PCO7YGXIOA6KQXBHSRGPEUHRU5ARLDRDDF7VF74JSTDUM2QKD4NXNQ = 1 << 3
Y6LZNPP33FDJYSFFG72QMET4WKJNNRIGQ2XSSO5676YXW572FRANDC3BGU = A5CXS7GCTRQQV2G7SFIVU3TI5KZKLUK7TC2GQ7UKMWKPKI6GWR65RFHJK4(A7RQQ7PC4WLBAFY3PDAOUOCME5DZMEAR5ZEFBNN2PQKYIAWOPIAA5QSL2J, P2VMOBU2U7GTIITCNEWRKSATW3CVBQJ5BZKSFEDWDWTJAAFI34KLK7U32U(M74NWW5W4BA35KFV6ROMY3XV4YASUZ733DEDSLTDLJTZUSQBDY7HYAFTVU(A7RQQ7PC4WLBAFY3PDAOUOCME5DZMEAR5ZEFBNN2PQKYIAWOPIAA5QSL2J) / XMW3PCO7YGXIOA6KQXBHSRGPEUHRU5ARLDRDDF7VF74JSTDUM2QKD4NXNQ) * XMW3PCO7YGXIOA6KQXBHSRGPEUHRU5ARLDRDDF7VF74JSTDUM2QKD4NXNQ, chr(0x3d))
ZCDX6JR53HGJHYQI6NKQASBSHTVTKYJ4GXG4RENEQS6IEFK3UYSRSSCTPL4 = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\U00000062a\x73e\U000000364")
ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7 = arr["\x6f\162\u0064"]
VRRYA7Q3RVJP4GGXKJJ2RWL3F5B3RFFI4TY3YL2A7CBOLG5CVD5EAMVWRM = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(ZCDX6JR53HGJHYQI6NKQASBSHTVTKYJ4GXG4RENEQS6IEFK3UYSRSSCTPL4)["\U0000006232\U00000064e\143\U0000006fd\145"]
CZ5EDUTIPYA32OYMZI2FHSPUAKIRV2FCDHLDCSLDJ42PUV4EU5QKO57R2A = VRRYA7Q3RVJP4GGXKJJ2RWL3F5B3RFFI4TY3YL2A7CBOLG5CVD5EAMVWRM(Y6LZNPP33FDJYSFFG72QMET4WKJNNRIGQ2XSSO5676YXW572FRANDC3BGU)
FHHTAJYJTNJTF2U7UODGZLRNRHTCD4KSFHJLWQZEATMLW4VJE2QW6OA2475 = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("\u006c\172m\141")
XGDOCXHDIYD4YMH7UZKGOYGEDDGO2FPO3QRELCML5KZTMAXMTG47T2K2I = arr["li\u0073\x74"]
ZH7DTVYSP5K53Q6BWIYSLETCPK75NDZX4QKJUONCOYBOJZCETPKRSUAVKW = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(FHHTAJYJTNJTF2U7UODGZLRNRHTCD4KSFHJLWQZEATMLW4VJE2QW6OA2475)["d\U00000065\x63\U0000006fm\u0070\162e\163\x73"]
D3U5DC4TIQARJSIOR6CJG7X3S2EYLBGDVSSJ7HNPYMLPS7IHQILQH7NNY6 = ZH7DTVYSP5K53Q6BWIYSLETCPK75NDZX4QKJUONCOYBOJZCETPKRSUAVKW(CZ5EDUTIPYA32OYMZI2FHSPUAKIRV2FCDHLDCSLDJ42PUV4EU5QKO57R2A)
OHZE337P5F643ACVSLUSMMTSS6TRJG6QFKFNTA7OWIIIQ4CE3PPN4FJHYL = M74NWW5W4BA35KFV6ROMY3XV4YASUZ733DEDSLTDLJTZUSQBDY7HYAFTVU(YQZ5ROM4W3C3E7OPYI6BN4XSGG6CAR76JZYGEV73NP6DEUT5MTQ3IVZGVP)
ENWGRMLCF5R2KMTPBXDXXFWYU2TR3MAQJIA7NZVGNW3P6GNIMGTFISZKVF = arr["b\171\U00000074\x65s"]
if OHZE337P5F643ACVSLUSMMTSS6TRJG6QFKFNTA7OWIIIQ4CE3PPN4FJHYL < 2:
XWIIZVLEG5Q7Y6DFZVGHO7SS2H6QXSOZABY3MHW63THWNNF5K7QQ3T5SED("N\x6ft\40\U00000065nou\147\u0068 \x61\u0072\147\u0075me\x6e\164\u0073")
ZA5OAQIR5BNYAB6Q4HSLDPFBKIAW3TPT3YXHESXRX4LHMH4FZHTP4Y57RX(1)
ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS = YQZ5ROM4W3C3E7OPYI6BN4XSGG6CAR76JZYGEV73NP6DEUT5MTQ3IVZGVP[1]
QCMBEDAI2CWIXIDOH5A5IS5QD6NB5XL2IX4FNFMUN52YJXXC4PCA23CQ7W = 7
SLKSRY3IAGFOYU3XNCWKHT66TZSP2GY6I5NMWYNCZ7WCVXHLDMIOEWJUQ6 = arr["\U00000065\166\U00000061l"]
E2UV76DGSKK7RSUPTTDQ2DUEKAYJ6Q7TGGCCM2MPN2OTGZDAFBSOZJEWZ22 = M74NWW5W4BA35KFV6ROMY3XV4YASUZ733DEDSLTDLJTZUSQBDY7HYAFTVU(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS)
if E2UV76DGSKK7RSUPTTDQ2DUEKAYJ6Q7TGGCCM2MPN2OTGZDAFBSOZJEWZ22 < QCMBEDAI2CWIXIDOH5A5IS5QD6NB5XL2IX4FNFMUN52YJXXC4PCA23CQ7W:
XWIIZVLEG5Q7Y6DFZVGHO7SS2H6QXSOZABY3MHW63THWNNF5K7QQ3T5SED("\U00000061r\U00000067\x20\151\x73\40t\U0000006f\157\x20\U00000073\x68\u006fr\164")
ZA5OAQIR5BNYAB6Q4HSLDPFBKIAW3TPT3YXHESXRX4LHMH4FZHTP4Y57RX(1)
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3 = XGDOCXHDIYD4YMH7UZKGOYGEDDGO2FPO3QRELCML5KZTMAXMTG47T2K2I(D3U5DC4TIQARJSIOR6CJG7X3S2EYLBGDVSSJ7HNPYMLPS7IHQILQH7NNY6)
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[235] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[0])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[515] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[1])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[507] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[2])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[519] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[3])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[1552] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[4])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[1586] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[5])
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[1617] = ZCGHHSY2HIUON3NW3VCQUCK5LGNAM62WNW47PAO46RFHVWVN4DG42JIV7(ZY6U6VY2PEVPRQLYCY47IPREAJQWM3CGARQLVLUYTDY3K4Z66FS3I5EQWS[6])
CFUHRDBDBYKZRQTLHSOLMASJAW5DE2A74SVFKIWRTFJCEIDO2OMZFB5QZ = ENWGRMLCF5R2KMTPBXDXXFWYU2TR3MAQJIA7NZVGNW3P6GNIMGTFISZKVF(SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3)
R57WEYHUFJ2L72N5HFD5NOKYEO2LGEMLPK5TAVNZLFTQYJMMVAWVMXYD6L = BSE4E6VG6QTGDVKPB2UN7IFD3XOBR4QRQJE5B42XSBPZ6OSZY2ZWJKXQTW3("m\x61\162\U00000073h\U00000061\154")
CNXXJNEWE4AYG2FRYIIESI35RR3IF4IXNTA4IJJ5N3QRIAAAAPN7GSZQFY = M6U5WMD4RLDNXPGONTPAGYB5LPFVTJVQ7FMQCF55WENLFC4T2VQ5TMAZ(R57WEYHUFJ2L72N5HFD5NOKYEO2LGEMLPK5TAVNZLFTQYJMMVAWVMXYD6L)["\x6co\u0061\U00000064\x73"]
O4SAAANABOIBGAAAV4ELSDVRYRT7WAQAAAAAABCZLI = CNXXJNEWE4AYG2FRYIIESI35RR3IF4IXNTA4IJJ5N3QRIAAAAPN7GSZQFY(CFUHRDBDBYKZRQTLHSOLMASJAW5DE2A74SVFKIWRTFJCEIDO2OMZFB5QZ)
try:
SLKSRY3IAGFOYU3XNCWKHT66TZSP2GY6I5NMWYNCZ7WCVXHLDMIOEWJUQ6(O4SAAANABOIBGAAAV4ELSDVRYRT7WAQAAAAAABCZLI)
except:
XWIIZVLEG5Q7Y6DFZVGHO7SS2H6QXSOZABY3MHW63THWNNF5K7QQ3T5SED("T\150\x69s\u0020\U00000069s \166e\162\u0079 un\x73t\u0061b\x6c\U00000065,\u0020\x68\165\U00000068?")
ZA5OAQIR5BNYAB6Q4HSLDPFBKIAW3TPT3YXHESXRX4LHMH4FZHTP4Y57RX(1)
Spending some time to deobfuscate it, we will get something similar like this:
import inspect, sys, re, math, base64, lzma, marshal
def main():
BLOCK_SIZE = 8
blocks = re.findall('_?([^\n?\\[\\]\']+?)\\s=', inspect.getsource(sys.modules['__main__']))
block = str.join("", blocks)
padded_block = str.ljust(block, math.ceil(len(block) / BLOCK_SIZE), chr(0x3d))
l2 = lzma.decompress(base64.b32decode(padded_block))
if len(sys.argv) < 2:
print("Not enough arguments")
exit(1)
if len(sys.argv[1]) < 7:
print("arg is too short")
exit(-1)
lst_l2 = list(l2)
lst_l2[235] = ord(argv[1][0])
lst_l2[515] = ord(argv[1][1])
lst_l2[507] = ord(argv[1][2])
lst_l2[519] = ord(argv[1][3])
lst_l2[1552] = ord(argv[1][4])
lst_l2[1586] = ord(argv[1][5])
lst_l2[1617] = ord(argv[1][6])
data = marshal.loads(bytes(lst_l2))
try:
eval(data)
except:
print("This is very unstable, huh?")
exit(-1)
if __name__ == '__main__':
main()
From the deobfuscated code we can see it looks like extracting base32 encoded text from itself using regex, pad it -> base32 decode -> decompress -> marshal -> eval. We also see that we need to provide an argument and the argument length must greater than 7.
Moreover, we also see that it did some changes on the code data based on our input before eval:
lst_l2 = list(l2)
lst_l2[235] = ord(argv[1][0])
lst_l2[515] = ord(argv[1][1])
lst_l2[507] = ord(argv[1][2])
lst_l2[519] = ord(argv[1][3])
lst_l2[1552] = ord(argv[1][4])
lst_l2[1586] = ord(argv[1][5])
lst_l2[1617] = ord(argv[1][6])
To investigate the code data, I append these into the stable.py
:
import dis
print(dis.code_info(O4SAAANABOIBGAAAV4ELSDVRYRT7WAQAAAAAABCZLI))
print(dis.dis(O4SAAANABOIBGAAAV4ELSDVRYRT7WAQAAAAAABCZLI))
After that, run it with any input you desire:
python3 stable.py 1234567
code_info output:
Name: <module>
Filename: <string>
Argument count: 0
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 4
Flags: NOFREE
Constants:
0: 10000
<SNIP>
99: 100099
100: 0
101: ('argv', 'exit')
102: ('sha256',)
103: 2
104: 'Usage: '
105: ' <passwd>'
106: 1
107: 9711424565004377232685487241741130660537817718394316136522340067127964227885525022420001661646939496718599540608081579347013032655061764133877156925827123786333371798400278520894719018619930240463131452457577184074345537847657937111838070040487887401817080124695053692442303799118926077741439148690994150505024166983196233674022816843702592633467144395379448888439860974578091544834372961055549393844525096383940669190823316777609479779106281969726063297357182244423570155395852153018645790744470507852109998305853670898441956711998028819529790437018901157587797430995171274861607645732279735938817560714148831826813
108: 14577897362821085283869313971595837572237381973528035997480016212662401635116
109: -34514
110: b'\x96094\xdc\x1e\xd0\xe0\xa5\xabs\x16\xcb5\xb5\xed\x04\xa2\xd8\xb0\x86_6\xdf\x8c\x81G#\xcc\x0b8\xfe[\x93\xae\x97\xa9T\x8d\x99\xe1Yj\xf2\xb2\x1e\xcf6\x93J\xd8\x81\xe9\x1fO\x84T?J\x9f\xaat\x05p>\xc8\x12\xf71wh\x13\xef\x9d\x7f\xf7\xcb\x867\x08\xf4I\x0f\xa7\x0e\xb9re\xd3_\xfa\xcd\xd1\xbfv\xcfB\x9a\x13\xcfx\xfd\xb4D/\xe914;\xf2c\xee\x96:\xf8 \xf9\xf6g\x8c\xd3\xfa\xcd\xb13\xb9\xdf\x04w\x0cF\xb4\xb6\xf8\x88\x02re\xe2Oq\xa56\xe1v7\x0b\xd2\xe6\xe0\xc4+\xb8\xdfH\xd9\xa8u\x8d\xa1\xd7]\xd5\xff\x81\xb6\x15\xfc\x89a#\xc8@\xdc,\xb0\xa9\x1e\xdc\xe84x\x94\xcb\x8dP\x15\x0br\x8d8\x9f\x9cHN\xa6\x08_\xf0\xc2\x1cF\xd6p;\xb1r\xcf\x94\xf8\x92]\xddq\xdc?\xea\xc7\xb6\xffN\x06AhL\xeb\xb0\xe2\xf7\xdcG\x08S5\xc2\xaa\xcd<J\x99\x8a\xae\xad\xcdO\x00\x19\x1e%x}e\x00\xbf5\x8d'
111: 'big'
112: <code object encrypt at 0x7fabfe4c4f50, file "<string>", line 128>
113: 'encrypt'
114: 'Correct password: '
115: 'Flag: '
116: 'Wrong password: '
117: 'Try again'
118: None
Names:
0: pad
1: sys
2: argv
3: exit
4: hashlib
5: sha256
6: len
7: print
8: passwd
9: enc_flag
10: flag_hash
11: e1
12: e
13: int
14: from_bytes
15: n
16: encrypt
17: dec_flag
18: digest
19: h
20: decode
dis (disassembly) output:
2 0 LOAD_CONST 0 (10000)
2 STORE_NAME 0 (pad)
<SNIP>
101 396 LOAD_CONST 99 (100099)
398 STORE_NAME 0 (pad)
102 400 LOAD_CONST 100 (0)
402 LOAD_CONST 101 (('argv', 'exit'))
404 IMPORT_NAME 1 (sys)
406 IMPORT_FROM 2 (argv)
408 STORE_NAME 2 (argv)
410 IMPORT_FROM 3 (exit)
412 STORE_NAME 3 (exit)
414 POP_TOP
104 416 LOAD_CONST 100 (0)
418 LOAD_CONST 102 (('sha256',))
420 IMPORT_NAME 4 (hashlib)
422 IMPORT_FROM 5 (sha256)
424 STORE_NAME 5 (sha256)
426 POP_TOP
105 428 LOAD_NAME 6 (len)
430 LOAD_NAME 2 (argv)
432 CALL_FUNCTION 1
434 LOAD_CONST 103 (2)
436 COMPARE_OP 0 (<)
438 POP_JUMP_IF_FALSE 234 (to 468)
106 440 LOAD_NAME 7 (print)
442 LOAD_CONST 104 ('Usage: ')
444 LOAD_NAME 2 (argv)
446 LOAD_CONST 100 (0)
448 BINARY_SUBSCR
450 FORMAT_VALUE 0
452 LOAD_CONST 105 (' <passwd>')
454 BUILD_STRING 3
456 CALL_FUNCTION 1
458 POP_TOP
108 460 LOAD_NAME 3 (exit)
462 LOAD_CONST 106 (1)
464 CALL_FUNCTION 1
466 POP_TOP
112 >> 468 LOAD_NAME 2 (argv)
470 LOAD_CONST 106 (1)
472 BINARY_SUBSCR
474 STORE_NAME 8 (passwd)
113 476 LOAD_CONST 51 (100051)
478 STORE_NAME 9 (enc_flag)
116 480 LOAD_CONST 108 (14577897362821085283869313971595837572237381973528035997480016212662401635116)
482 STORE_NAME 10 (flag_hash)
117 484 LOAD_CONST 50 (100050)
486 STORE_NAME 11 (e1)
120 488 LOAD_CONST 52 (100052)
490 LOAD_NAME 11 (e1)
492 BINARY_ADD
494 STORE_NAME 12 (e)
128 496 LOAD_NAME 13 (int)
498 LOAD_METHOD 14 (from_bytes)
500 LOAD_CONST 110 (b'\x96094\xdc\x1e\xd0\xe0\xa5\xabs\x16\xcb5\xb5\xed\x04\xa2\xd8\xb0\x86_6\xdf\x8c\x81G#\xcc\x0b8\xfe[\x93\xae\x97\xa9T\x8d\x99\xe1Yj\xf2\xb2\x1e\xcf6\x93J\xd8\x81\xe9\x1fO\x84T?J\x9f\xaat\x05p>\xc8\x12\xf71wh\x13\xef\x9d\x7f\xf7\xcb\x867\x08\xf4I\x0f\xa7\x0e\xb9re\xd3_\xfa\xcd\xd1\xbfv\xcfB\x9a\x13\xcfx\xfd\xb4D/\xe914;\xf2c\xee\x96:\xf8 \xf9\xf6g\x8c\xd3\xfa\xcd\xb13\xb9\xdf\x04w\x0cF\xb4\xb6\xf8\x88\x02re\xe2Oq\xa56\xe1v7\x0b\xd2\xe6\xe0\xc4+\xb8\xdfH\xd9\xa8u\x8d\xa1\xd7]\xd5\xff\x81\xb6\x15\xfc\x89a#\xc8@\xdc,\xb0\xa9\x1e\xdc\xe84x\x94\xcb\x8dP\x15\x0br\x8d8\x9f\x9cHN\xa6\x08_\xf0\xc2\x1cF\xd6p;\xb1r\xcf\x94\xf8\x92]\xddq\xdc?\xea\xc7\xb6\xffN\x06AhL\xeb\xb0\xe2\xf7\xdcG\x08S5\xc2\xaa\xcd<J\x99\x8a\xae\xad\xcdO\x00\x19\x1e%x}e\x00\xbf5\x8d')
502 LOAD_CONST 111 ('big')
504 CALL_METHOD 2
506 STORE_NAME 15 (n)
138 508 LOAD_CONST 112 (<code object encrypt at 0x7fabfe4c4f50, file "<string>", line 128>)
510 LOAD_CONST 113 ('encrypt')
512 MAKE_FUNCTION 0
514 STORE_NAME 16 (encrypt)
140 516 LOAD_NAME 16 (encrypt)
518 LOAD_NAME 9 (enc_flag)
520 CALL_FUNCTION 1
522 STORE_NAME 17 (dec_flag)
141 524 LOAD_NAME 5 (sha256)
526 LOAD_NAME 17 (dec_flag)
528 CALL_FUNCTION 1
530 LOAD_METHOD 18 (digest)
532 CALL_METHOD 0
534 STORE_NAME 19 (h)
143 536 LOAD_NAME 13 (int)
538 LOAD_METHOD 14 (from_bytes)
540 LOAD_NAME 19 (h)
542 LOAD_CONST 111 ('big')
544 CALL_METHOD 2
546 STORE_NAME 19 (h)
144 548 LOAD_NAME 19 (h)
550 LOAD_NAME 10 (flag_hash)
552 COMPARE_OP 2 (==)
554 EXTENDED_ARG 1
556 POP_JUMP_IF_FALSE 297 (to 594)
145 558 LOAD_NAME 7 (print)
560 LOAD_CONST 114 ('Correct password: ')
562 LOAD_NAME 8 (passwd)
564 FORMAT_VALUE 0
566 BUILD_STRING 2
568 CALL_FUNCTION 1
570 POP_TOP
147 572 LOAD_NAME 7 (print)
574 LOAD_CONST 115 ('Flag: ')
576 LOAD_NAME 17 (dec_flag)
578 LOAD_METHOD 20 (decode)
580 CALL_METHOD 0
582 FORMAT_VALUE 0
584 BUILD_STRING 2
586 CALL_FUNCTION 1
588 POP_TOP
590 LOAD_CONST 118 (None)
592 RETURN_VALUE
148 >> 594 LOAD_NAME 7 (print)
596 LOAD_CONST 116 ('Wrong password: ')
598 LOAD_NAME 8 (passwd)
600 FORMAT_VALUE 0
602 BUILD_STRING 2
604 CALL_FUNCTION 1
606 POP_TOP
608 LOAD_NAME 7 (print)
610 LOAD_CONST 117 ('Try again')
612 CALL_FUNCTION 1
614 POP_TOP
616 LOAD_CONST 118 (None)
618 RETURN_VALUE
Disassembly of <code object encrypt at 0x7fabfe4c4f50, file "<string>", line 128>:
130 0 LOAD_GLOBAL 0 (pow)
2 LOAD_FAST 0 (m)
4 LOAD_GLOBAL 1 (e)
6 LOAD_GLOBAL 2 (n)
8 CALL_FUNCTION 3
10 STORE_FAST 1 (c)
132 12 LOAD_FAST 1 (c)
14 LOAD_METHOD 3 (to_bytes)
16 LOAD_FAST 1 (c)
18 LOAD_METHOD 4 (bit_length)
20 CALL_METHOD 0
22 LOAD_CONST 1 (7)
24 BINARY_ADD
26 LOAD_CONST 2 (8)
28 BINARY_FLOOR_DIVIDE
30 LOAD_CONST 3 ('big')
32 CALL_METHOD 2
34 RETURN_VALUE
None
Wrong password: 1234567
Try again
From the disassembly output, I create a source code based on it:
import hashlib
import math
from Crypto.Util.number import bytes_to_long
def encrypt(enc_flag):
e = 100050 + 100052
b = b'\x96094\xdc\x1e\xd0\xe0\xa5\xabs\x16\xcba\xb5\xed\x04\xa2\xd8\xb0\x86_6\xdf\x8c\x81G#\xcc\x0b8\xfe[\x93\xae\x97\xa9T\x8d\x99\xe1Yj\xf2\xb2\x1e\xcfa\x93J\xd8\x81\xe9\x1fO\x84T?J\x9f\xaat\x05p>\xc8\x12\xf71wh\x13\xef\x9d\x7f\xf7\xcb\x86a\x08\xf4I\x0f\xa7\x0e\xb9re\xd3_\xfa\xcd\xd1\xbfv\xcfB\x9a\x13\xcfx\xfd\xb4D/\xe914;\xf2c\xee\x96:\xf8 \xf9\xf6g\x8c\xd3\xfa\xcd\xb13\xb9\xdf\x04w\x0cF\xb4\xb6\xf8\x88\x02re\xe2Oq\xa56\xe1v7\x0b\xd2\xe6\xe0\xc4+\xb8\xdfH\xd9\xa8u\x8d\xa1\xd7]\xd5\xff\x81\xb6\x15\xfc\x89a#\xc8@\xdc,\xb0\xa9\x1e\xdc\xe84x\x94\xcb\x8dP\x15\x0br\x8d8\x9f\x9cHN\xa6\x08_\xf0\xc2\x1cF\xd6p;\xb1r\xcf\x94\xf8\x92]\xddq\xdc?\xea\xc7\xb6\xffN\x06AhL\xeb\xb0\xe2\xf7\xdcG\x08S5\xc2\xaa\xcd<J\x99\x8a\xae\xad\xcdO\x00\x19\x1e%x}e\x00\xbf5\x8d'
n = int.from_bytes(b,'big')
c = pow(enc_flag,e,n)
dec_flag = int.to_bytes(c, math.floor(c.bit_length() + 7 / 8), 'big')
return dec_flag
flag_hash = 14577897362821085283869313971595837572237381973528035997480016212662401635116
enc_flag = 100051
dec_flag = encrypt(enc_flag)
h = int.from_bytes(hashlib.sha256(dec_flag).digest(), 'big')
if h == flag_hash:
print('Correct password:', passwd)
print('Flag:', dec_flag.decode())
else:
print('Wrong password:', passwd)
print('Try again')
From the source code, we know that it is performing RSA encryption using enc_flag
with the value of 100051
and later compare with flag_hash
to determine whether the provided password is correct.
At this point, we have to investigate where our input will affect the behavior of the code.
Before that, looking into code_info
output, we see there is a constant is not being used and it looks like typical encrypted blob, so we can assume that its actual position should belong to enc_flag
:
107: 9711424565004377232685487241741130660537817718394316136522340067127964227885525022420001661646939496718599540608081579347013032655061764133877156925827123786333371798400278520894719018619930240463131452457577184074345537847657937111838070040487887401817080124695053692442303799118926077741439148690994150505024166983196233674022816843702592633467144395379448888439860974578091544834372961055549393844525096383940669190823316777609479779106281969726063297357182244423570155395852153018645790744470507852109998305853670898441956711998028819529790437018901157587797430995171274861607645732279735938817560714148831826813
With some trial & error, we know our third character of input must be k
(ASCII value 107) because it will assign the constant value properly to enc_flag
:
So let’s change the stable.py
:
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[507] = 107
Through more trial & error, we also know that the second and fourth characters entered will affect e1
and thus e
because:
117 484 LOAD_CONST 50 (100050)
486 STORE_NAME 11 (e1)
120 488 LOAD_CONST 52 (100052)
490 LOAD_NAME 11 (e1)
492 BINARY_ADD
494 STORE_NAME 12 (e)
In RSA, the most common value of e
is 65537
, so we can assume that the e
is using 65537
. If not, we have to bruteforce with combination of other constants. From the output of code_info
, we can use the constants below to get 65537
:
51: 100051
<SNIP>
109: -34514
Let’s update stable.py
again:
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[515] = 51
<SNIP>
SFBE2D5Q64KKE3V6ID5E7IY34DKU3IH47JQRIPAEQ2776MF2UBZOTS2II3[519] = 109
Moving on, after some more investigation, the first byte can be any value and the last three bytes of our input will affect the n
, hence we have to bruteforce it.
With these information obtained, we can write a script to decrypt it:
from Crypto.Util.number import long_to_bytes
import tqdm
c = 9711424565004377232685487241741130660537817718394316136522340067127964227885525022420001661646939496718599540608081579347013032655061764133877156925827123786333371798400278520894719018619930240463131452457577184074345537847657937111838070040487887401817080124695053692442303799118926077741439148690994150505024166983196233674022816843702592633467144395379448888439860974578091544834372961055549393844525096383940669190823316777609479779106281969726063297357182244423570155395852153018645790744470507852109998305853670898441956711998028819529790437018901157587797430995171274861607645732279735938817560714148831826813
for b1 in tqdm.tqdm(range(255)):
for b2 in range(255):
for b3 in range(255):
b = b'\x96094\xdc\x1e\xd0\xe0\xa5\xabs\x16\xcb' + b1.to_bytes(1,'little') + b'\xb5\xed\x04\xa2\xd8\xb0\x86_6\xdf\x8c\x81G#\xcc\x0b8\xfe[\x93\xae\x97\xa9T\x8d\x99\xe1Yj\xf2\xb2\x1e\xcf' + b2.to_bytes(1,'little') + b'\x93J\xd8\x81\xe9\x1fO\x84T?J\x9f\xaat\x05p>\xc8\x12\xf71wh\x13\xef\x9d\x7f\xf7\xcb\x86' + b3.to_bytes(1,'little') + b'\x08\xf4I\x0f\xa7\x0e\xb9re\xd3_\xfa\xcd\xd1\xbfv\xcfB\x9a\x13\xcfx\xfd\xb4D/\xe914;\xf2c\xee\x96:\xf8 \xf9\xf6g\x8c\xd3\xfa\xcd\xb13\xb9\xdf\x04w\x0cF\xb4\xb6\xf8\x88\x02re\xe2Oq\xa56\xe1v7\x0b\xd2\xe6\xe0\xc4+\xb8\xdfH\xd9\xa8u\x8d\xa1\xd7]\xd5\xff\x81\xb6\x15\xfc\x89a#\xc8@\xdc,\xb0\xa9\x1e\xdc\xe84x\x94\xcb\x8dP\x15\x0br\x8d8\x9f\x9cHN\xa6\x08_\xf0\xc2\x1cF\xd6p;\xb1r\xcf\x94\xf8\x92]\xddq\xdc?\xea\xc7\xb6\xffN\x06AhL\xeb\xb0\xe2\xf7\xdcG\x08S5\xc2\xaa\xcd<J\x99\x8a\xae\xad\xcdO\x00\x19\x1e%x}e\x00\xbf5\x8d'
n = int.from_bytes(b,'big')
e = 0x10001
m = pow(c,e,n)
flag = long_to_bytes(m)
if b"wgmy" in flag:
print(flag)
After some while, we will get the flag:
Flag:
wgmy{bc2d2a189608a8614e99afdd49bc5c49}
Overall this is a very good challenge, unfortunately I wasn’t able to solve it during the competition despite spending whole day on it. Nevertheless, I learned a lot and thanks Catz so much for the challenge.