第19回セキュリティさくらの資料(前半だけ)です。
OSSͷ੬ऑੑΛ୳ͨ͢Ίʹͬͨ͜ͱୈ19ճηΩϡϦςΟ͘͞ΒTeppei Fukuda
View Slide
ൃද༰• ͕ࣗOSSͷ੬ऑੑΛ୳ͨ͢Ίʹͬͨ͜ͱ• ٳͷதʹٸʹDoSͷ੬ऑੑΛݟ͚ͭͨ͘ͳͬͯɺே·Ͱ͔͚ͯ୳ͨ͠ͱ͖ͷ• ͜Ε͔ΒͬͯΈ͍ͨਓ͚ͷ
ࣗݾհ• ా మฏʢ@knqyf263ʣ• ͖ͳͷ• ωοτϫʔΫ• ηΩϡϦςΟ• ອը• ߪಡࡶࢽ• िץগδϟϯϓ• िץগϚΨδϯ• िץগαϯσʔ• िץϠϯάδϟϯϓ• δϟϯϓSQ• ผগϚΨδϯ• ଞɺ୯ߦຊଟ
OSSͷ੬ऑੑΛ୳ͯ͠Έͨ
ηΩϡϦςΟΤϯδχΞͷҿΈձ͖ͳ੬ऑੑʁ͖ͳϓϩτίϧʁ͖ͳϨΠϠʔʁ ͖ͳೋԿళʁ
͖ͳ੬ऑੑʁ
͖ͳ੬ऑੑʹ͍ͭͯߟ͑Δ• ৗʹ੬ऑੑใΛ͏• IPA, JPCERT/CC, JVN, NVD• RSSʢ֤छχϡʔεαΠτϒϩάʣ• Twitter, Facebook, etc.
੬ऑੑ͕ଟ͗͢ΔํੑΛߜΔ
੬ऑੑͷϨΠϠʔ• WebΞϓϦέʔγϣϯ• CGI, PHP, Java, etc• ϛυϧΣΞ• OpenSSH• Postfix, etc.• OS ʢWindows, LinuxͳͲʣ• Linux Kernel
੬ऑੑͷछྨ• WebΞϓϦέʔγϣϯ• XSS, SQLi, CSRF, etc.• ϛυϧΣΞ• ҙίʔυ࣮ߦ, ೝূճආ, etc.• OS ʢWindows, LinuxͳͲʣ• ݖݶঢ֨, DoS, etc.
$43'ʹڵຯ͕͋Δʂ• CSRFͷख๏ʹ͍ͭͯௐΔ• ʑͷ੬ऑੑใΛ͏ςϯγϣϯ্͕Βͳ͔ͬͨ• ௐͨΒ͋Μ·Γͳ࣌͋Δ• ผͷ੬ऑੑΛௐΔ
3$&ʹڵຯ͕͋Δʂ• RCEͷख๏ʹ͍ͭͯௐΔ• ʑͷ੬ऑੑใΛ͏͖ͩʂ• Ͳ͏͍͏ιϑτΣΞͰͷɹRCE͕͖͔ߟ͑Δ
͖ͳιϑτΣΞʹ͍ͭͯߟ͑Δ• ීஈ͍ͬͯΔͷ͔Βߟ͑Δ• WebΞϓϦέʔγϣϯϑϨʔϜϫʔΫ• ίϚϯυϥΠϯπʔϧ• Office, Adobe, etc.• ৭ʑͬͯΈΔ• GitHubͷTrendingʹ͕͋ͬͨπʔϧͳͲ
͖ͳϓϩτίϧɾن֨ʹ͍ͭͯߟ͑Δ• HTTP• ϔομ• ೝূʢBasicೝূɺDigestೝূʣ• SSL/TLS• DNS• WEP/WPA2• Bluetooth, etc.
৭ʑͳํ͔Β͖ͳͷΛߟ͑Δ• ͖ͳ੬ऑੑʹ͍ͭͯߟ͑Δ• ͖ͳιϑτΣΞʹ͍ͭͯߟ͑Δ• ͖ͳϓϩτίϧʹ͍ͭͯߟ͑ΔΔؾ͕ग़Δͷ͕Ұ൪େࣄʂʂ࣠৭ʑ ʢ͓͖ۚͳΒใۚͷଟՉͱ͔ʣ
ࣗͷ߹• ͖ͳϓϩτίϧ• DNS• ͖ͳιϑτΣΞ• BIND• ͖ͳ੬ऑੑ• DoSBINDͷDoS୯७ͳ%P4ͳͷʹக໋తͳײ͕͖͡
·ͣաڈͷ੬ऑੑ͔ΒֶͿ
طଘͷ੬ऑੑΛݕূ͢Δ• ެ։͞Ε͍ͯΔPoCΛࢼ͢• KRACK (WPA2ͷ੬ऑੑʣ• https://github.com/vanhoefm/krackattacks-scripts• ίϛοτΛݟͯ੬ऑੑͷݪҼΛཧղ͢Δ• https://w1.fi/cgit/hostap/commit/?id=a00e946c1c9a1f9cc65c72900d2a444ceb1f872e• ࣗͰPoCΛॻ͍ͯΈΔ
DoSͷ੬ऑੑ• ͲΜͳ߹ʹDoS͕ى͖Δ͔ʁ• CWE-129 ྻࢦඪͷෆదͳݕূ• CWE-131 ޡͬͨόοϑΝʔɾαΠζͷܭࢉ• ଞʹଟ
ରΛܾΊΔ
͖ͳͷ͔Β୳࢝͠ΊΔྫ• ͖ͳϓϩτίϧ• SSL/TLS• ͖ͳιϑτΣΞ• OpenSSL• ͖ͳ੬ऑੑ• ҙίʔυ࣮ߦʢRCE)աڈͷ੬ऑੑͱࣅͨͷ͕ͳ͍͔
͖ͳͷ͔Β୳࢝͠ΊΔྫ• ͖ͳϓϩτίϧ• SSL/TLS• ͖ͳιϑτΣΞ• OpenSSL• ͖ͳ੬ऑੑ• ଞͷ੬ऑੑ୳ͯ͠ΈΔ• DoS҉߸ͷ࣮ෆඋաڈʹݟ͔͍ͭͬͯΔ
͖ͳͷ͔Β୳࢝͠ΊΔྫ• ͖ͳϓϩτίϧ• SSL/TLS• ͖ͳιϑτΣΞ• BoringSSL/LibreSSL͔Β୳ͯ͠ΈΔ• ͖ͳ੬ऑੑ• ҙίʔυ࣮ߦྨࣅͷιϑτΣΞ
ࣗͷ߹• ͖ͳϓϩτίϧ• DNS• ͖ͳιϑτΣΞ• DNSܥͷιϑτΣΞΛ୳ͯ͠ΈΔ• https://github.com/miekg/dns• https://github.com/kenshinx/godns• ͖ͳ੬ऑੑ• DoS࠷ۙ(PΛॻ͘͜ͱ͕ଟ͍ͷͰ(PͰॻ͔Εͨͷ͔Β୳͢
ࣗͷ߹• ͖ͳϓϩτίϧ• DNS• ͖ͳιϑτΣΞ• ωοτϫʔΫϞχλϦϯά͢Δπʔϧ͖• ͖ͳ੬ऑੑ• DoS
ωοτϫʔΫHPMBOH%P4
Packetbeat
Packetbeat• golangͰॻ͔ΕͨOSS• ϦΞϧλΠϜͰωοτϫʔΫͷτϥϑΟοΫΛղੳ• Logstash·ͨElasticsearchʹσʔλΛૹ৴͢ΔɺܰྔͳωοτϫʔΫύέοτΞφϥΠβʔ• ରԠϓϩτίϧ• HTTP• DNS• MySQL• PostgreSQL
୳࢝͠ΊΔલʹҰൠతͳ·ͱΊ
੬ऑੑஅ• ϒϥοΫϘοΫεܕ• ಈ࡞͍ͯ͠ΔରγεςϜʹର͠ɺ࣮ࡍʹٖࣅతͳ৵ೖɾ߈ܸΛֻ͚Δ• ϗϫΠτϘοΫεܕ• γεςϜͷߏιʔείʔυͳͲͷࡉ͔͍ใͳͲΛͱʹஅ͢Δ
OSSͷ߹• ϒϥοΫϘοΫεܕ• ಈ͔͠ํ͕ஸೡʹॻ͍ͯ͋Δʢ͜ͱ͕ଟ͍ʣͷͰ؆୯• ϗϫΠτϘοΫεܕ• ιʔείʔυެ։͞Ε͍ͯΔιʔείʔυΛमਖ਼ͯ͠ಈ͔͢͜ͱ͢ΒՄೳʂʂ
ྲྀΕ1. υΩϡϝϯτΛಡΜͰ༷ΛѲ2. ࣮ࡍʹಈ͔ͯ͠ڍಈΛѲ3. աڈͷ੬ऑੑͳͲΛࢀߟʹ͋ͨΓΛ͚ͭΔ4. ιʔείʔυΛͪΒͬͱಡΉ5. ֎෦͔Βෆਖ਼ͳೖྗΛ༩͑ͯΈͨΓ͢Δ6. ίʔυΛमਖ਼ͯ͠ಈ͔ͨ͠ΓσόοΨͬͨΓ
υΩϡϝϯτΛಡΉ• ύέοτΛղੳͯ͠σʔλΛอଘͯ͘͠ΕΔ• LogstashElasticsearchʹૹΔ• HTTPͷεςʔλείʔυSQLͷΫΤϦͳͲΛύʔεͯ͠औಘͯ͘͠ΕΔInternetϛϥʔϦϯάPacketbeatElasticsearch
࣮ࡍʹಈ͔ͯ͠ΈΔ$./packetbeat -N -e2017/12/01 04:03:08.679303 metrics.go:23: INFO Metrics logging every 30s2017/12/01 04:03:08.679117 beat.go:297: INFO Home path: [/root/packetbeat-5.6.3-linux-x86_64] Config path: [/root/packetbeat-5.6.3-linux-x86_64] Data path: [/root/packetbeat-5.6.3-linux-x86_64/data] Logs path: [/root/packetbeat-5.6.3-linux-x86_64/logs]2017/12/01 04:03:08.680873 beat.go:192: INFO Setup Beat: packetbeat; Version:5.6.32017/12/01 04:03:08.680879 publish.go:217: INFO Dry run mode. All output typesexcept the file based one are disabled.... (தུʣ...2017/12/01 04:03:08.681143 protos.go:89: INFO registered protocol plugin: mysql2017/12/01 04:03:08.681145 protos.go:89: INFO registered protocol plugin: nfs2017/12/01 04:03:08.681148 protos.go:89: INFO registered protocol plugin: pgsql2017/12/01 04:03:08.681165 protos.go:89: INFO registered protocol plugin: redis2017/12/01 04:03:08.707989 beat.go:233: INFO packetbeat start running.
աڈͷ੬ऑੑͳͲΛࢀߟʹ͋ͨΓΛ͚ͭΔ• https://www.elastic.co/jp/community/securityPacketbeatͷաڈͷ੬ऑੑ0݅ͩͬͨ
աڈͷ੬ऑੑͳͲΛࢀߟʹ͋ͨΓΛ͚ͭΔྨࣅOSSͰύέοτղੳՕॴͰDoS͕ى͖͍͢ύʔαͷιʔείʔυΛಡΜͰΈΔ
աڈͷ੬ऑੑͳͲΛࢀߟʹ͋ͨΓΛ͚ͭΔո͍͠
ιʔείʔυΛಡΜͰ͍͘
PostgreSQLϓϩτίϧͷύʔεॲཧfunc pgsqlFieldsParser(s *pgsqlStream, buf []byte) error {...ʢதུʣ...// read Type OID (int32)off += 4// read column length (int16)off += 2// read type modifier (int32)off += 4// read format (int16)format := common.BytesNtohs(buf[off : off+2])off += 2fieldsFormat = append(fieldsFormat, byte(format))}ྻͷΠϯσοΫεͷࢦఆ͕ؾʹͳΔbufʹԿ͕ೖΔʁ
ιʔείʔυͷྲྀΕΛ͏QHTRM'JFMET1BSTFSQBSTF3PX%FTDSJQUJPOQBSTF$PNNBOEfunc (pgsql *pgsqlPlugin) parseCommand(s *pgsqlStream) (bool, bool) {// read typetyp := byte(s.data[s.parseOffset])...switch typ {case 'Q':return pgsql.parseSimpleQuery(s, length)case 'T':return pgsql.parseRowDescription(s, length)...}ύέοτͷCZUF͕`5ͷͱ͖ʹݺΕΔॲཧ
testdb=# SELECT * FROM test;id | body----+------1 | test(1 row)RowDescription3PX%FTDSJQUJPO• parseRowDescription• ͦͦRowDescriptionͱʁ
RowDescription• Byte1('T')• ϝοηʔδ͕ߦͷهड़Ͱ͋Δ͜ͱΛࣝผ͠·͢ɻ• Int32• ࣗΛؚΉɺϝοηʔδ༰ͷ͞ʢόΠτ୯Ґʣɻ• Int16• ߦͷϑΟʔϧυΛࢦఆ͠·͢ ʢθϩͱ͢Δ͜ͱ͕Ͱ͖·͢ʣɻ• ͦͷޙɺ֤ϑΟʔϧυʹରͯ͠ҎԼ͕ଓ͖·͢ɻ• String• ϑΟʔϧυ໊Ͱ͢ɻ• Int32• ϑΟʔϧυ͕ಛఆͷςʔϒϧͷྻͱͯࣝ͠ผͰ͖Δ߹ɺςʔϒϧͷΦϒδΣΫτIDͰ͢ɻ ͞ͳ͘θϩͰ͢ɻ• Int16• ϑΟʔϧυ͕ಛఆͷςʔϒϧͷྻͱͯࣝ͠ผͰ͖Δ߹ɺྻͷଐੑ൪߸Ͱ͢ɻ ͞ͳ͘θϩͰ͢ɻ• Int32• ϑΟʔϧυͷσʔλܕͷΦϒδΣΫτIDͰ͢ɻ• Int16• σʔλܕͷେ͖͞ʢpg_type.typlenΛࢀরʣͰ͢ɻ ෛͷ͕ՄมͷܕΛද͢͜ͱʹҙ͍ͯͩ͘͠͞ɻ• Int32• ܕम০ࢠʢpg_attribute.atttypmodΛࢀরʣͰ͢ɻ म০ࢠͷҙຯܕʹݻ༗Ͱ͢ɻ• Int16• ϑΟʔϧυʹ༻͞ΕΔॻࣜίʔυͰ͢ɻݱࡏɺ0ʢςΩετʣ·ͨ1ʢόΠφϦʣͷ͍ͣΕ͔ʹͳΓ·͢ɻhttps://www.postgresql.jp/document/9.6/html/protocol-message-formats.htm
testdb=# SELECT * FROM test;id | body----+------1 | test---54 00 00 00 32 00 02 69 64 00 00 00 45 0e 00 01 00 00 00 17 00 04 ff ff ff ff00 00 62 6f 64 79 00 00 00 45 0e 00 02 00 00 00 19 ff ff ff ff ff ff 00 00---54 ! 'T'ͳͷͰRowDescription00 00 00 32 ! ͞=50Λҙຯ͢Δ00 02 ! ϑΟʔϧυ=2Λҙຯ͢Δ69 64 00 ! "id"00 00 45 0e ! ςʔϒϧͷΦϒδΣΫτID(17678)00 01 ! ྻͷଐੑ൪߸...00 00 ! ϑΟʔϧυʹ༻͞ΕΔॻࣜίʔυʢςΩετͷ߹0ʣRowDescriptionCVGͷॲཧ͜͜ʂ
OSSͳΒͰͷ୳͠ํ• σόοΨΛར༻͢Δ• printfσόοά͢Δ• ςετίʔυΛར༻͢ΔςετίʔυؾܰʹҟৗͳೖྗΛ༩͑ΒΕΔͷͰΦεεϝ
// Test parsing a response with data attachedfunc TestPgsqlParser_dataResponse(t *testing.T) {if testing.Verbose() {logp.LogInit(logp.LOG_DEBUG, "", false, true, []string{"pgsql", "pgsqldetailed"})}pgsql := pgsqlModForTests()data := []byte(“5400000033000269640000008fc40001000000170004ffffffff000076616c75650000008fc4000200000019ffffffffffff0000" +"44000000130002000000013100000004746f746f" +"440000001500020000000133000000066d617274696e" +"440000001300020000000134000000046a65616e" +"430000000b53454c45435400" +"5a0000000549")message, err := hex.DecodeString(string(data))if err != nil {t.Error("Failed to decode hex string")}stream := &pgsqlStream{data: message, message: new(pgsqlMessage)}ok, complete := pgsql.pgsqlMessageParser(stream)ςετίʔυ͜͜Λ͍ͬͯ͡ΈΔ
// Test parsing a response with data attachedfunc TestPgsqlParser_dataResponse(t *testing.T) {if testing.Verbose() {logp.LogInit(logp.LOG_DEBUG, "", false, true, []string{"pgsql","pgsqldetailed"})}pgsql := pgsqlModForTests()data := []byte("540000001b00016964000000450e0001000000170004ffffffff0000")message, err := hex.DecodeString(string(data))if err != nil {t.Error("Failed to decode hex string")}stream := &pgsqlStream{data: message, message: new(pgsqlMessage)}ok, complete := pgsql.pgsqlMessageParser(stream)ςετίʔυਖ਼ৗͳΛೖΕͯΈΔ
// Test parsing a response with data attachedfunc TestPgsqlParser_dataResponse(t *testing.T) {if testing.Verbose() {logp.LogInit(logp.LOG_DEBUG, "", false, true, []string{"pgsql","pgsqldetailed"})}pgsql := pgsqlModForTests()data := []byte("540000001b00016964000000450e0001000000170004ffffffff0000")message, err := hex.DecodeString(string(data))if err != nil {t.Error("Failed to decode hex string")}stream := &pgsqlStream{data: message, message: new(pgsqlMessage)}ok, complete := pgsql.pgsqlMessageParser(stream)ෆਖ਼ͳೖྗʹม͑ΔCVGCZUFલఏͳͷͰͬͨΒམͪͦ͏
$ cd ~/go/src/github.com/elastic/beats/packetbeat/protos/pgsql$ go test -run TestPgsqlParser_dataResponsePASSok github.com/elastic/beats/packetbeat/protos/pgsql0.085sམͪͳ͔ͬͨςετίʔυΛ࣮ߦͯ͠ΈΔ
͜ͷՕॴݺΕ͍ͯͳ͔ͬͨprintfσόοάͯ͠ΈΔfunc (pgsql *pgsqlPlugin) parseCommand(s *pgsqlStream) (bool, bool) {// read typetyp := byte(s.data[s.parseOffset])...case 'T':+ fmt.Printf("%d\n", length)return pgsql.parseRowDescription(s, length)...}ଞͷՕॴͷΤϥʔॲཧͰ͔Ε͍ͯΔ
͞ͷൺֱif len(s.data[s.parseOffset:]) <= length {detailedf("Wait for more data")return true, false}σʔλͷ࣮ࡍͷ͞ccCZUFύέοτͷهड़ccCZUF54 00 00 00 1b ...• ҎԼͷνΣοΫͰ͔Ε͍ͯͨ
// Test parsing a response with data attachedfunc TestPgsqlParser_dataResponse(t *testing.T) {if testing.Verbose() {logp.LogInit(logp.LOG_DEBUG, "", false, true, []string{"pgsql","pgsqldetailed"})}pgsql := pgsqlModForTests()data := []byte("540000001b00016964000000450e0001000000170004ffffffff0000")message, err := hex.DecodeString(string(data))if err != nil {t.Error("Failed to decode hex string")}stream := &pgsqlStream{data: message, message: new(pgsqlMessage)}ok, complete := pgsql.pgsqlMessageParser(stream)͞Λἧ͑ΔΔCZUFݮΒ͢ B
$ go test -run TestPgsqlParser_dataResponsepanic: runtime error: slice bounds out of range [recovered]panic: runtime error: slice bounds out of rangegoroutine 20 [running]:testing.tRunner.func1(0xc420068820)/usr/lib/go-1.8/src/testing/testing.go:622 +0x29dpanic(0x9bc500, 0xe46af0)/usr/lib/go-1.8/src/runtime/panic.go:489 +0x2cfgithub.com/elastic/beats/packetbeat/protos/pgsql.pgsqlFieldsParser(0xc420035f60, 0xc421164f05,0x16, 0x16, 0x0, 0x0)/root/go/src/github.com/elastic/beats/packetbeat/protos/pgsql/parse.go:384 +0x6c6... (தུ) ...created by testing.(*T).Run/usr/lib/go-1.8/src/testing/testing.go:697 +0x2caexit status 2FAIL github.com/elastic/beats/packetbeat/protos/pgsql 0.084sམͪͨʂʂςετίʔυΛ࣮ߦͯ͠ΈΔ
ಉ༷ʹ͍͔ͯͭ͘͠ͷΤϥʔॲཧΛ͢Γൈ͚Δͱ...
$ echo -e "T\x00\x00\x00\x1a\(ࣗॗʣxff\xff\xff\xff\x00" | nc 127.0.0.1 5432؆୯ύέοτΛ1ͭ͛Δ͚ͩͰམͪΔ# ./packetbeat -N -e...2017/12/04 04:45:22.465965 log.go:145: ERR Stacktrace: goroutine 13 [running]:runtime/debug.Stack(0xbd68c4, 0x2b, 0xc421249560)/usr/local/go/src/runtime/debug/stack.go:24 +0x79github.com/elastic/beats/libbeat/logp.Recover(0xbc366f, 0x14)/go/src/github.com/elastic/beats/libbeat/logp/log.go:145 +0x138panic(0xada5c0, 0xc4200100f0)/usr/local/go/src/runtime/panic.go:458 +0x243github.com/elastic/beats/packetbeat/protos/pgsql.pgsqlFieldsParser(0xc4211e8b00, 0xc421210749, 0x2f,0x36, 0xbba4b1, 0xd)/go/src/github.com/elastic/beats/packetbeat/protos/pgsql/parse.go:382 +0x53c...
मਖ਼Օॴ͞ͷ֬ೝΛ͢Δ͚ͩhttps://github.com/elastic/beats/pull/5457
ҙ• ੬ऑੑΛݟ͚͍͖ͭͯͳΓPull RequestΛ͛ΔͷΊ·͠ΐ͏• ଞͷਓݟ͑ΔͷͰɺθϩσΠ߈ܸʹͳͬͯ͠·͍·͢• దͳܦ࿏Ͱใࠂ͠·͠ΐ͏• ϝʔϧʢϝʔϧͰௐ͔ͯ͠ΒPRΛ͛ͨΓʣ• ػؔΛ௨͢ʢIPAJPCERT/CCͳͲʣ
Security updatehttps://discuss.elastic.co/t/beats-5-6-4-security-update/106739
·ͱΊOSSͷ੬ऑੑΛ୳࣌͢ʹͬͨ͜ͱ• ͖ͳͷʹࢥ͍ΛͤΔ• ੬ऑੑɾιϑτΣΞɾϓϩτίϧɾ͓ۚɹ• ͖ͳͷʹؔ࿈͢ΔιϑτΣΞɾ੬ऑੑ͋ͨΓ͔Β୳ͯ͠ΈΔ• աڈͷ੬ऑੑ͔ΒֶͿʢࣅͨͷ͕ݟ͔ͭΓ͍͢ʣ• OSSͳΒͰͷํ๏Λར༻͢Δ• ςετίʔυσόοΨ• ࠓճDoSʹ͍ͭͯൃද͠·͕ͨ͠ɺಉ༷ͷํ๏Ͱଞʹز͔ͭใࠂ͠·ͨ͠