7 minute read

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:

IMG

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:

IMG

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.